-->
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.  [ 5 posts ] 
Author Message
 Post subject: reordering collections -- apologies for rehash...
PostPosted: Tue Jun 20, 2006 12:44 am 
Beginner
Beginner

Joined: Mon May 09, 2005 5:26 pm
Posts: 21
after a great deal of searching, i've found a number of related questions, but I'm not sure I've found an answer -- if it's out there, i apologize.

the class Foo has a collection of Bar. Bar implements comparable on a couple of fields, say kingdom and phylum. a thing may be reclassified (swap phylum -- ok, not very likely, but there you are), and thus change its sorting.

i have Foo mapped with:
Code:
<set name="bars" table="foo_bar_join" lazy="false" sort="natural" cascade="all,delete-orphan">
   <key column="fooID" not-null="true"/>
   <many-to-many column="barID" class="Bar"/>
</set>


should the sorting change and an attempt be made to persist it, i get the oft-cited:

Code:
Violation of PRIMARY KEY constraint 'PK__foo_bar_join__7A9C383C'. Cannot insert duplicate key in object 'foo_bar_join'


because Hibernate (as advertised) tries to do the INSERT prior to the DELETE.

in: http://forum.hibernate.org/viewtopic.php?t=945944, Gavin states:

Quote:
All you have to do is remove and re-add any element that was changed. At maximum, all you need to do is copy all elements, clear() the original instance, and addAll().
[/url]

but in: http://forum.hibernate.org/viewtopic.php?p=2231436, he states:

Quote:
usually wanting to delete then reinsert is a sign that you are trying to use Hibernate in the wrong way


so my questions are:
1) what the heck is one supposed to do to reorder a collection? i.e. what is the right way to do this in a Hibernate-approved manner?
2) if delete/flush/insert is the answer, how do i know what to delete if Hibernate is managing the collection (if i change the above mapping to lazy="true")?

other relevent posts seem to include:
http://forum.hibernate.org/viewtopic.php?t=958514
http://forum.hibernate.org/viewtopic.php?t=959869

thanx all for any input.

-don.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 20, 2006 1:05 am 
Regular
Regular

Joined: Tue May 16, 2006 3:32 am
Posts: 117
Quote:
Violation of PRIMARY KEY constraint 'PK__foo_bar_join__7A9C383C'. Cannot insert duplicate key in object 'foo_bar_join'


Another reason for this error could be that Bar also has a collection of Foo with inverse="false". At least one of the collections either in Foo or Bar should have an inverse="true".

As far as sorting is concerned, I generally use a Comparator. Did not face any problems so far.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 20, 2006 3:10 am 
Beginner
Beginner

Joined: Mon May 09, 2005 5:26 pm
Posts: 21
regretably, no. Foo to Bar is one-way only. thanks for the thought though.

when you say that you've not seen any problems so far, does that include instances where you've changed the sort order of detached objects? if so, i'd love to see sample code/mappings.

thanx again for input,

-don.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 20, 2006 8:40 am 
Regular
Regular

Joined: Tue May 16, 2006 3:32 am
Posts: 117
Quote:
when you say that you've not seen any problems so far, does that include instances where you've changed the sort order of detached objects?


No, not really. Where we did change the order, we had used a list or used a simple set and sorted it with Collections.sort when required.

I tried out a simple Foo---manytomany --Bar (one way) example to see if I could reproduce the error you were getting. Bar implements comparable on a single ‘name’ field. Mapping in Foo is similar to yours. The ‘equals’ method is based on the id. I am assuming that you would have something similar. So the ordering maintained by the set is not consistent with equals. “The behavior of a set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.”

After a bit of hit and trial, I found a case that results in an exception (not the same as yours though).

1. Retrieve a Foo from the database that has 2 or more Bars.
2. Remove one of them from the set
3. Set the name same as one of the Bars that are still in the Foo.bars collection.
4. Add it back to Foo.bars
5. Save Foo.

So long as you set a name that is totally different there is no exception.

The only way I found to fix the above case was to modify the Bar’s compareTo method and return compareTo on the ids when the names were equal else return compareTo on the name. This fixed the above exception and also when the saved object is retrieved again, both the bars with equal names appear in the set.

Coming back to the exception you are getting, I feel that there is some error in your code. I can’t really see how this exception can occur in the above case.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 20, 2006 10:31 am 
Beginner
Beginner

Joined: Mon May 09, 2005 5:26 pm
Posts: 21
thanks JayeshJ,

my Bar class doesn't use the id in its equals(), nor am i trying to create two Bars that are equals() and both included in the Set (the kingdom/phylum isn't really what i'm using -- obviously a great many animals would compare as equals() if using only kingdom/phylum) but you're correct in seeing that something *is* wrong w/ my compareTo()/equals(). for reference, Bar looks like:

Code:
private String name;
private int kingdom;
private int phylum;

...

public int compareTo(Object o) {
   Bar that = (Bar) o;
   int result = kingdom - that.getKingdom();
   if (result != 0) return result;
   return phylum- that.getPhylum();
}
public boolean equals(Object o) {
   if (this == o) return true;
   if (! (o instanceof Bar)) return false;
   Bar that = (Bar) o;
   return ( (kingdom == that.getKingdom()) && (phylum== that.getPhylum()) );
}
public int hashCode() {
   return 17*kingdom + 37*phylum;
}


which is wrong-ish since the kingdom/phylum aren't immutable and are used in compareTo(), equals(), and hashCode(). I understand that this will mess up any Set containing Bars that change while in the Set and because of that, it is perhaps not very good code. The above methods should ideally be written against immutable fields only.

the sequence of events that generates the problems is:
- persist a foo with a collection of Bars
- rearrange the collection as so:

Code:
Collection<Bar> barcopy = new HashSet<Bar>();
for (Bar bar: foo.getBars()) {
   if (bar.getPhylum() == 5) bar.setPhylum(2);
   barcopy.add(bar);
}
foo.getBars().clear();   
foo.getBars().addAll(barcopy);


- merge the foo with the session
- commit the changes.

again, this isn't the real code, just a simple test case. there's only one entry in the collection that starts out with a phylum=5 and none that start out w/ a phylum=2.

if, between the clear() and addAll(), i perform a merge/commit it works because the clear() will delete everything from the DB and duplicate keys are then impossible (i'm forcing DELETE before INSERT).

how does this differ from how your non-exception-raising code works?

to the community: what is the *right* way to do what i'm trying? since, in the real problem, the reordering will be done on the client side for a bunch of Bars, must i really go through *each* collection replacing all elemnets with a clear()/merge()/flush()/addAll()/merge()/flush() cycle? this will be more than problematic, not just in DB inefficiency, but because the client would then become responsible for managing the collection.

up till now, everything i've done w/ Hibernate *just works* and has made managing of relationships quite simple (once i started to get the hang of it). so, i'm *certain* this is a problem w/ the way i'm doing something -- the Hibernate folks have anticipated seemingly any reasonable need and reordering a collection seems a reasonable need.

thanks again,

-don.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 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.