views:

248

answers:

6

I would like to hold a ref of some other entity using its identifier.

The object model which I am working on is huge and related entities hold lot of data. Also few pieces are cyclic in nature and I suspect that holding objects directly will be a problem.

So consider following as an example say there are two entities A & B:

Class A
{
private String id;
private String name;
private String type;
private String b_id // A reference to instance of B without holding direct instance
}

Class B
{
private String id;
private String name;
private String type;
}

How do I achieve this using Hibernate framework?

+2  A: 

What you've written there is two different classes with no relation to each other, and that's how Hibernate will treat them.

When you read an instance of A from Hibernate, you'll then have to go back to Hibernate to make a request for the instance of B that corresponds to A's b_id.

I strongly recommend that you use normal object references and give Hibernate chance to do its job. Don't write it off in advance because you don't think it will cope.

skaffman
+1  A: 

You are basically ignoring the power the ORM can give you.

Remember that a.getB().getId() will not initialize the B instance! It's only when you actually access additional methods and properties of B (or you explicitly queried for it) that a (good) ORM like Hibernate will actually bother fetching B.

Max Rydahl Andersen
Yes but I need to ship those objects to a client via SOAP so client needs to have a way in which further information about associated entity ie B can be queried so I need to either provide a ref to B (which means having another object model for SOAP communication) or 'complete' object A ie All fields of object A plus 'complete' object B
Chetan
Sounds like you need to have a DAO (Dataaccess object).Neither Hibernate nor anything else are able to persist objects without having references to the instances...You can always have two mappings for a class - a RealA and a partialA one mapped correctly and the other just as pure values. You can use entity-name to have multiple mappings for the same class.
Max Rydahl Andersen
A: 

Thanks for your Ans. Few things I would like to note,

  • Class A & B are related but the only way I know to tell Hibernate about the relation is by using the normal object reference. I would like to know if theres any way in which I can related class A and class B without having to keep instance of B in A. Can I have an id of B in A in object model however in Database have b_id as a foreign key B --> A

  • I would definitely like Hibernate to take care of the situation but the depth of ref chain is so much that query to one object will result into lot of info flowing through because of direct instance members. So I thought one way to avoid that would be place only ref of other entities in classes without compromising on data integrity by placing relationships through foreign keys.

  • Also some piece of data+relationship form a graph like structure. Holding direct instance references could also be a problem. I dont know how Hibernate works in such situations though.

Chetan
Sorry for posting this as an ans but comments limit you to 600 chars.
Chetan
A: 

Sounds like your use case does not require Hibernate but something else as you would basically be "un-inventing the wheel" here. Your only option is to lazy-load B so that when you call A, B and its object graph gets pulled ONLY when you explicitly call getB().

non sequitor
+1  A: 

I'm having the same problem and am wanting to be able to set up keys but not use all this lazy load stuff. I find hibernate to be so fragile with errors and mysterious ghost classes all over the place. So far it's not simplifying my life. Consider this:

model (grossly simplified):

Portfolio {
 ...
 @OneToMany(cascade = {CascadeType.ALL}, fetch=FetchType.EAGER )
 @MapKey(name="symbol")
 public Map<String,Position> positions;
}
Position {
    String symbol; int quantity;
}
Trade {
    String symbol; int quantity;  // price,etc
 ...
 @ManyToOne() // looser coupling here.
 public Portfolio portfolio;
}

test case (note: all 'enterTrade()' does is stick the trade in a trade table and adjust the position size in the portfolio, 'undoTrade()' just reverts):

Trade trade1 = new Trade("GOOG", 25, portfolio);
Trade trade2 = new Trade("GOOG", 50, portfolio);
portfolio = portfolioDAO.enterTrade(trade1);
portfolio = portfolioDAO.enterTrade(trade2);

portfolio = portfolioSource.undoTrade(trade1, user);
portfolio = portfolioSource.undoTrade(trade2, user);
Assert.assertFalse(portfolio.getPositions().containsKey("GOOG"),
        "after both trades net a position of zero");

This test should work right? WRONG! Mysteriously there are still 25 GOOG shares in the portfolio.

The basic problem is that the trade2 object points to a portfolio object that becomes stale. The last undoTrade() operations uses a portfolio that still has 75 shares of GOOG. Note that Trade2 is never stale because only persist() has been executed on it. But when update() is performed on Portfolio, an extra copy is created rather than changing the original object (as is done with 'persist()'). I'm having trouble understanding why that design would not be considered stupid and error prone. This is a simple example. It's not like we're talking about concurrent access issues or anything fancy. Oh, and when I try to refresh trade2, get LazyInitializationException which is completely baffling.

So, my solution is to use keys so that every need to refresh is explicit:

Trade {
    ...
    @ManyToOne(targetEntity=Portofio.class)
    @JoinColumn(referenceColumnName="id")
    public Long portfolio_id;
}

Or whatever. While I'm expressing much frustration here, I would love to hear an honest explanation of how to deal with these problems. Thanks.

Carlos
I solved my core problem by going down to the Hibernate level (I'm using JPA): Session session = (Session) entityManager.getDelegate(); session.update(p); //return entityManager.merge(p);Turns out that the Hibernate API is far superior and clearer as to what's actually going on. I think I may switch entirely to that.
Carlos
A: 

OK, so I know this is an old thread, but this is fairly common and most hibernate people don't seem to realize that it is a problem for those of us that can't keep the session open throughout the request.

Do something like this (using annotations as an example since that is what I'm familiar with):


Class A
{
private String id;
private String name;
private String type;

@OneToOne(fetch=FetchType.LAZY)
private B b;
@Column(name="b_id", insertable=false, updatable=false)
private String b_id;
}

This will result in b not being fetched by default, but b_id will be available even after the session is closed and running your result through something like dozer or gilead. The mapping for b_id is set to insertable=false and updatable=false so that it is read only and will be ignored when writing back to the database. To update the association you must set the actual b field.

Sean