Hello,
Please help me figure out this problem... the tag-team of lazyInitializationException and nonUniqueObjectException is putting the smackdown on me! :-)
One of my Hibernate apps under development is a web-based subscription management system. I have two many-to-many associations between Subscriber and Publication:
(class Subscriber):
Code:
<set
name="subscribedPublications"
table="subscription"
lazy="true"
inverse="true"
cascade="none"
access="property"
>
<key column="subscriber_id"/>
<many-to-many column="publication_id"
class="com.wrinkledog.phydeaux.model.Publication"/>
</set>
<set
name="unsubscribedPublications"
table="optout"
lazy="true"
inverse="true"
cascade="none"
access="property"
>
<key column="subscriber_id"/>
<many-to-many column="publication_id"
class="com.wrinkledog.phydeaux.model.Publication"/>
</set>
(...and class Publication):
Code:
<set
name="subscribers"
table="subscription"
access="field"
lazy="true"
cascade="none"
>
<key column="publication_id"/>
<many-to-many
column="subscriber_id"
class="com.wrinkledog.phydeaux.model.Subscriber"
/>
</set>
<set
name="optouts"
table="optout"
access="field"
lazy="true"
cascade="none"
>
<key column="publication_id"/>
<many-to-many
column="subscriber_id"
class="com.wrinkledog.phydeaux.model.Subscriber"
/>
</set>
Notes:
(1) There are no cascading operations;
(2) I'm aware that this might not be the most performant mapping, and it's possible that one end of these might get changed to a bag/list... but that's not the problem right now...
The user is presented with a form containing a checkbox for each available publication. When the form is submitted, we get a new session, re-attach the Subscriber object, and iterate through the available publications processing the value of the checkbox. So, the code looks like this:
Code:
session.saveOrUpdate (subscriber);
Iterator i = subscriptionBindings.iterator();
while (i.hasNext()) {
SubscriptionBinding subscriptionBinding = (SubscriptionBinding) i.next();
Publication publication = subscriptionBinding.getPublication();
session.saveOrUpdate (publication);
/*
* ...and then we either subscribe or unsubscribe the user.
* This touches all 4 collections (the 2 in the Subscriber and
* the 2 in the Publication).
*/
So here's what happens: in one iteration of the loop, the subscribe/unsubscribe operation triggers lazy loading of the Subscriber's sets, and Publication objects are loaded, e.g. right here:
Code:
@4000000042546b3501d6256c 16:05:15,027 DEBUG SessionImpl:3218 - initializing collection [com.wrinkledog.phydeaux.model.Subscriber.unsubscribedPublications#4f2252b203194e5a0103195114600001]
@4000000042546b35027130d4 16:05:15,030 DEBUG SessionImpl:3219 - checking second-level cache
@4000000042546b35028d57dc 16:05:15,040 DEBUG SessionImpl:3225 - collection not cached
@4000000042546b3502abd48c 16:05:15,042 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
@4000000042546b3502c674f4 16:05:15,044 DEBUG SQL:237 - select unsubscrib0_.publication_id as publicat2___, unsubscrib0_.subscriber_id as subscrib1___ from optout unsubscrib0_ where unsubscrib0_.subscriber_id=?
@4000000042546b3502c868f4 Hibernate: select unsubscrib0_.publication_id as publicat2___, unsubscrib0_.subscriber_id as subscrib1___ from optout unsubscrib0_ where unsubscrib0_.subscriber_id=?
@4000000042546b3502e877fc 16:05:15,046 DEBUG BatcherImpl:241 - preparing statement
@4000000042546b35030a1d44 16:05:15,048 DEBUG StringType:46 - binding '4f2252b203194e5a0103195114600001' to parameter: 1
@4000000042546b350338da1c 16:05:15,051 DEBUG Loader:327 - result set contains (possibly empty) collection: [com.wrinkledog.phydeaux.model.Subscriber.unsubscribedPublications#4f2252b203194e5a0103195114600001]
@4000000042546b350354515c 16:05:15,053 DEBUG SessionImpl:2984 - uninitialized collection: initializing@4000000042546b3503737dd4 16:05:15,055 DEBUG Loader:197 - processing result set
@4000000042546b35038e1284 16:05:15,057 DEBUG Loader:405 - result row:
@4000000042546b3503adcf84 16:05:15,059 DEBUG StringType:68 - returning '4f2252b203194e5a0103195114600001' as column: subscrib1___
@4000000042546b3503c8393c 16:05:15,061 DEBUG Loader:292 - found row of collection: [com.wrinkledog.phydeaux.model.Subscriber.unsubscribedPublications#4f2252b203194e5a0103195114600001]
@4000000042546b3503e60624 16:05:15,062 DEBUG SessionImpl:3007 - reading row
@4000000042546b350408a184 16:05:15,064 DEBUG LongType:68 - returning '4' as column: publicat2___
@4000000042546b350424b8ec 16:05:15,067 DEBUG SessionImpl:1950 - loading [com.wrinkledog.phydeaux.model.Publication#4]
@4000000042546b3504681aec 16:05:15,068 DEBUG Loader:405 - result row:
@4000000042546b3504add67c 16:05:15,073 DEBUG StringType:68 - returning '4f2252b203194e5a0103195114600001' as column: subscrib1___
Then, on a subsequent iteration of the loop, saveOrUpdate is called on this Publication:
Code:
@4000000042546b35259c690c 16:05:15,628 DEBUG SessionImpl:1363 - saveOrUpdate() previously saved instance with id: 4
@4000000042546b3525b4272c 16:05:15,630 DEBUG SessionImpl:1411 - updating [com.wrinkledog.phydeaux.model.Publication#4]
@4000000042546b35272e634c "file:/Volumes/data1/wd/projects/LPEA/phydeaux/dev/webapp/modules/fido/flow.js", line 38: uncaught JavaScript exception:
@4000000042546b35272e7ea4 at subscribe (file:/Volumes/data1/wd/projects/LPEA/phydeaux/dev/webapp/modules/fido/flow.js, Line 38):
@4000000042546b35272e8e44 net.sf.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: 4, of class: com.wrinkledog.phydeaux.model.Publication
Of course, if I comment out the call to saveOrUpdate(publication), then I get lazyInitializationException (if the publication is not yet in either of the Subscribers' Sets).
Can anybody help me work out the solution to this problem?
Thanks,
— mark
Hibernate version: 2.1.8
[/code]