-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 
Author Message
 Post subject: Mutable set elements and Object.equals
PostPosted: Wed Mar 10, 2004 1:19 pm 
Beginner
Beginner

Joined: Fri Oct 10, 2003 10:12 am
Posts: 39
hi all,

i have a few questions regarding the way that hibernate uses sets and what the best practices are. i have stumbled across a problem that has made me question using sets anymore.

i'll give an example - i've got a persistent class Account with a one-to-many relationship with another persistent class Order, here's the mapping:

<class name="my.package.Account" table="Account"
...
<set name="orders"
lazy="true"
inverse="true"
cascade="none" >
<key column="account" />
<one-to-many class="my.package.Order" />
</set>
...
</class>

<class name="my.package.Order" table="Orders">
...
<many-to-one name="account"
class="my.package.Account"
column="account"
not-null="true" />
...
</class>


So now, i load an account, find it's orders and then edit an order. I have overridden equals and hashcode in Order, so when i edit the order i won't be able to find it in the set due to a different hashcode. I could edit my Order's equals method to disregard all fields barring the id but that would mean i could no longer use equals for checking the equality of two non-referencial-identical orders.

I was also wondering if someone could explain how hibernate manages this behaviour. say i use the method above and then happen to add the same order to the set which results in two elements of the same object. on update, hibernate will encounter the same object twice, i assume it will update the object twice too. what about on save? will hibernate have populated the object with an id and then perform an update on the second occurance?

it seems to me that the solution is to use maps instead, indexed by the primary key, but then how can i add an unsaved object to the map - this would mean i would have to save the object before adding it to the map.

how about if in equals i include all the fields for equality, but in hashcode i only have the id in the computation? would this cause problems elsewhere?

thanks,
cam


Top
 Profile  
 
 Post subject: solution?
PostPosted: Wed Mar 10, 2004 1:28 pm 
Beginner
Beginner

Joined: Fri Oct 10, 2003 10:12 am
Posts: 39
ok i might have found a solution - i do as questioned last, override equals with all fields but hashcode with only the id, but in the hashcode funtion also add the hashcode of the type's class, this way the equals-hashcode relationship is maintained. any comments?
cam


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 1:47 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
See here:

http://www.hibernate.org/109.html

It's a deep and subtle topic... but it's very possible that what you actually want is to *not* override equals and hashCode *at all*. See the wiki page for more details. If it is still confusing and you feel it could be clearer, feel free to edit it to clarify it ;-)

Cheers!
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 1:48 pm 
Hibernate Team
Hibernate Team

Joined: Tue Sep 09, 2003 2:10 pm
Posts: 3246
Location: Passau, Germany
Take a look at http://www.hibernate.org/109.html


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 6:40 pm 
Beginner
Beginner

Joined: Fri Oct 10, 2003 10:12 am
Posts: 39
thanks for the link - i must have looked at an old copy of that page before.
But there is something i don't get, in the round-up it says that if you use a business key for you're equals and hashcode then you can have multiple new instances in a set, how?

if i have the following class:

class Order {

Long id;
Date orderDate;

public Order(){
}

public Long getId(){
return id;
}

public Date getOrderDate(){
return date;
}

public boolean equals(Object obj){
if(!(obj instanceof Order.class))
return false;
Order order = (Order)obj;
return orderDate == null ? order.orderDate == null :
order.equals(order.orderDate);
}

public in hashCode(){
return 39 + 17* orderDate == null ? 1 : orderDate.hashCode();
}

...
}


this is using a (simplistist) business-key method described, but if i then create two Orders and add them to the same set, both orders will have the same hashcode and be equal so there will be an overwrite.

does they're solution require that at least some of the fields are set before inserting into a set, or have i missed something?

thanks, cam


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 6:47 pm 
Beginner
Beginner

Joined: Fri Oct 10, 2003 10:12 am
Posts: 39
sorry, just thought of something else.

the business-key pattern doesn't solve my original problem where i edit a persistent object in a set and then try to remove that object from the set.

sorry, this behaviour must seem a little strange but i'm developing a client app where these circumstances could crop up unless i limit user control somehow.

thank cam


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 7:34 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
Yes, if your business key is identical and you use your business key for equals/hashcode, then if you add two identically-business-keyed objects to a Set, they will overwrite each other. The wiki page doesn't say that won't happen.

But what the wiki page *does* say is that if you have two *differently* business-keyed objects that use the business key for equals/hashcode, and you add them both to a set, they *won't* overwrite each other.

The wiki page's first example is showing that if you use persistent id for equals/hashcode, then all your objects of the same class will be equal, and they will all overwrite each other in a set no matter what their business key is.

Regarding your last post: your business key must be something about the object that is not editable -- i.e. something where if you edit it, you are essentially creating a *different object*. The equals/hashcode contract states that hashcode will not change as long as the object has the same identity. This is the contract you have to abide by.

It is still not clear to me why you want to override equals and hashcode AT ALL. If you just use native Java object identity, it may simply work for you. Try it.

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 11, 2004 7:51 am 
Beginner
Beginner

Joined: Fri Oct 10, 2003 10:12 am
Posts: 39
thanks for the clarification.

i guess the problem is that i am using a new session for each request, so if i am to edit an object, i'll first load the object in one session, fetch lazy collections in another session, save the changed objects in another session. this could result in multiple objects with the same id.

take the senario where i load an account and it's collection of orders. i then load an order's order items with associated products. i then want to change an order item's product, so i load up the choices of products and assign a new product to an order item. this then updates the inventory of the old product and new product. i could then have multiple instances of the same product with different attributes getting passed into the final session to save all changes.

just by working through this, it seems i am over complicating matters. i was under the impresssion that a session should be as short as possible, in effect a session per request approach.

what do you suggest i should do? what if i wrap the whole editing procedure in a single long-lived transaction instead of one transcation per session as i am using now?

i guess the reason i think to close a session as soon as possible is that the hibernate debug complains on unclosed sessions but it seems that having a single open session and transaction for the duration of the edit would solve all the issues with object identity.

doesn't this mean i could no longer use the thread local pattern as the updates are sent in a new thread?

thanks, cam


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 11, 2004 1:17 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
Look into session.saveOrUpdate() and session.lock(). They support "disconnected operation" which is essentially what you are talking about here; you want to reassociate previously loaded objects with a new session in a controlled way. It's definitely possible. No, you likely won't want to keep a session open for a very long time.

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 12, 2004 8:47 am 
Beginner
Beginner

Joined: Fri Oct 10, 2003 10:12 am
Posts: 39
thanks for the info - i also read you're posts on the recent thread in the application design forum and the combination has given me a good overview of what the options are.
thanks cam


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.