-->
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.  [ 3 posts ] 
Author Message
 Post subject: Cascade updates using detached objects
PostPosted: Tue Sep 21, 2004 11:54 am 
Newbie

Joined: Tue Sep 21, 2004 9:26 am
Posts: 2
Hi,
During cascase updates (parent/child bi-directional relationship), hibernate tries to
insert the new child elements (added to the child collection) before
deleting child elements removed from the child collection.
This leads to an issue in my application as described below.

I am using a detached object that I send to the client.
The user may delete a child element from the UI.
At that point application removes that child element from the parent's collection (java.util.Set).
Within the same session the user adds the same child element again.
Application adds the same element to the parent collection (with same primary key fields).
Now when my application tries to update the detached parent object it
runs into primary key violation as hibernate tries to insert the new child element
before deleting the old one. (Note: My legacy child table uses uses natural primary keys (does not have surrogate keys))
Even if we use surrogate keys, there would be unique key violation for the business keys defined in the table.

kindly advice how to resolve this.

Hibernate version:2.1

Mapping documents:

<hibernate-mapping>
<class
name="com.model.Booking"
table="Booking">
<id name="id" column="bookingId" unsaved-value="0">
<generator class="assigned"/>
</id>
<timestamp column="journalTimestamp" name="timestamp" unsaved-value="null"/>
<property name="salesPerson" column="salesPerson" not-null="false" unique="false" update="true" insert="true" />
<set name="commodities" cascade="all-delete-orphan" inverse="true">
<key column="bookingId" />
<one-to-many class="com.model.Commodity" />
</set>
</class>
</hibernate-mapping>


<hibernate-mapping>
<class
name="com.model.Commodity"
table="Commodity" dynamic-update="true">
<composite-id name="primaryKey" unsaved-value="any" class="com.model.CommodityPK">
<key-many-to-one name="booking" class="com.model.Booking" column="bookingId"/>
<key-property name="commodityCode" column="commodityCode"/>
<key-property name="lineItemNumber" column="lineItemNumber"/>
</composite-id>
<timestamp column="journalTimestamp" name="timestamp" unsaved-value="null"/>
<property name="quantity" column="quantity" not-null="false" unique="false" update="true" insert="true" />
</class>
</hibernate-mapping>

Code between sessionFactory.openSession() and session.close():
1st Session - load object
tx = session.beginTransaction();
booking = (Booking) session.load(Booking.class, new Integer(1));
tx.commit();

2nd Session - update object
tx = session.beginTransaction();
session.update(booking);
tx.commit();

Code between the load and update sessions:
//booking is the detached object obtained from the load session
//remove the commodity with line item 1 and commodity code "COAL"
Iterator it = booking.getCommodities().iterator();
Commodity delComm = null;
while (it.hasNext()) {
delComm = (Commodity) it.next();
if ( (delComm.getPrimaryKey().getCommodityCode().equals("COAL")) && (delComm.getPrimaryKey().getLineItemNumber() == 1 ) ) {
break;
}
}
booking.getCommodities().remove(delComm);

//add the same commodity again
CommodityPK pk1 = new CommodityPK();
pk1.setBooking(booking);
pk1.setLineItemNumber(1);
pk1.setCommodityCode("COAL");
Commodity comm1 = new Commodity();
comm1.setPrimaryKey(pk1);
comm1.setQuantity(1000);
booking.getCommodities().add(comm1);

java classes:
public class Booking {
private int id;
private String salesPerson;
private Set commodities;
private Timestamp timestamp;
//Getters and setters are implemented in actual code
}

public class CommodityPK implements Serializable {
private Booking booking;
private String commodityCode;
private int lineItemNumber;
//Getters and setters are implemented in actual code
//hashcode and equals is implemented in actual code
}

public class Commodity {
private CommodityPK primaryKey;
private int quantity;
private Timestamp timestamp;
//Getters and setters are implemented in actual code
}

Full stack trace of any exception that occurs:
[java] Hibernate: select booking0_.bookingId as bookingId0_, booking0_.journalTimestamp as journalT2_0_, booking0_.salesPerson as salesPer3_0_ from Booking booking0_ where booking0_.bookingId=?
[java] Hibernate: select commoditie0_.bookingId as bookingId__, commoditie0_.commodityCode as commodit2___, commoditie0_.lineItemNumber as lineItem3___, commoditie0_.bookingId as bookingId0_, commoditie0_.commodityCode as commodit2_0_, commoditie0_.lineItemNumber as lineItem3_0_, commoditie0_.journalTimestamp as journalT4_0_, commoditie0_.quantity as quantity0_ from Commodity commoditie0_ where commoditie0_.bookingId=?
[java] booking Id1
[java] commodity codeCOAL
[java] commodity Line Item1
[java] commodity quantity()100
[java] booking Id1
[java] commodity codeCOAL
[java] commodity Line Item2
[java] commodity quantity()200
[java] Hibernate: insert into Commodity (journalTimestamp, quantity, bookingId, commodityCode, lineItemNumber) values (?, ?, ?, ?, ?)
[java] net.sf.hibernate.JDBCException: could not insert: [com.model.Commodity#com.model.CommodityPK@1fa599]
[java] at net.sf.hibernate.persister.EntityPersister.insert(EntityPersister.java:478)
[java] at net.sf.hibernate.persister.EntityPersister.insert(EntityPersister.java:442)
[java] at net.sf.hibernate.impl.ScheduledInsertion.execute(ScheduledInsertion.java:29)
[java] at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java:2414)
[java] at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java:2367)
[java] at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2236)
[java] at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
[java] at hello.HelloWorld.main(Unknown Source)
[java] Caused by: java.sql.SQLException: Unique constraint violation: in statement [insert into Commodity (journalTimestamp, quantity, bookingId, commodityCode, lineItemNumber) values (?, ?, ?, ?, ?)]
[java] at org.hsqldb.jdbc.jdbcUtil.throwError(Unknown Source)
[java] at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
[java] at net.sf.hibernate.impl.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:22)
[java] at net.sf.hibernate.persister.EntityPersister.insert(EntityPersister.java:468)
[java] ... 7 more
[java] Hibernate: select booking0_.bookingId as bookingId0_, booking0_.journalTimestamp as journalT2_0_, booking0_.salesPerson as salesPer3_0_ from Booking booking0_ where booking0_.bookingId=?
[java] Hibernate: select commoditie0_.bookingId as bookingId__, commoditie0_.commodityCode as commodit2___, commoditie0_.lineItemNumber as lineItem3___, commoditie0_.bookingId as bookingId0_, commoditie0_.commodityCode as commodit2_0_, commoditie0_.lineItemNumber as lineItem3_0_, commoditie0_.journalTimestamp as journalT4_0_, commoditie0_.quantity as quantity0_ from Commodity commoditie0_ where commoditie0_.bookingId=?

_________________
ROHIT BAHL
J2EE Developer


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 21, 2004 12:54 pm 
Regular
Regular

Joined: Tue Jun 22, 2004 8:01 pm
Posts: 106
Location: PowderTown, Utah, USA
Don't know if this helps or not, but here's my 2 cents.

I've seen the same problem you're describing. In my case it had to do with the fact that hibernate wraps a collection with a wrapper that detects removal and insertion so that it can syncrhonize all the operations.

Hibernate takes the Set/List semantics of Java collections literally. If you're dealing with a "loaded" object rather than a new one, the collection will be a wrapper that is capable of recording the operations performed. So, for example, if you remove an item from said collection and immediately re-add it, hibernate will follow suit and when the flush occurs it will remove the relationship and then re-add it, which can take the form of setting a field to null, or even deleting it entirely if the collection is set to remove orphans.

Also, you can't fool it by copying all your objects into a different collection like:

Set workingSet = new HashSet(hibernateObject.getSomeSet());
workingSet.remove(someObject);
workingSet.add(someObject);
hibernateObject.setSomeSet(workingSet); // exception thrown here...

You'll get an exception.

Here's what works for me: If I've got to do lots of add/remove operations (say for reordering purposes) then I use a different client collection, make the changes, then compare the two collections and only make the minimum changes. Basically use a scratchpad collection and them be smart about making changes to the real hibernated collection.

BTW, this problem seemed to happen often when using List collections rather than Sets. I was trying to use the List mapping to model a sequenced collection but ended up going back to using Sets because moving items around in the list resulted in the problem you describe.


Top
 Profile  
 
 Post subject: Cascade updates using detached objects
PostPosted: Wed Oct 06, 2004 9:10 am 
Newbie

Joined: Tue Sep 21, 2004 9:26 am
Posts: 2
Hi ,
I agree with you.
I am using a temporary collection to make the changes, then I compare the two collections and only make the required changes to the original collection of the detached object.

thanks for the advice.

_________________
ROHIT BAHL
J2EE Developer


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