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
|