Thanks, Anthony, your answer shows that I am on the right way, as this is exactly what I am doing at the moment. More precisely:
1-Session A: get objects
2-Session A: show these object in your UI
3-No Session: user modifies objects:
I keep track of all modified/updated and deleted objects.
4-Session B: reattached objects with new session:
I use saveOrUpdate
5-Session B: do some version check:
My config contains a timestamp property
6-Session B: (flush):
dito
However, I get strange exceptions, so I was doubting whether I adopted the right strategy. The exception is:
Code:
[ERROR] JDBCException: 38 could not delete collection rows: [IFEntry.parents#121]
java.sql.SQLException: Try to insert null into a non-nullable column: column: CHILD_ID table: ENTRY_ENTRY in statement [update ENTRY_ENTRY set CHILD_ID=null where CHILD_ID=? and PARENT_ID=? and CHILD_ID=?]
at org.hsqldb.jdbc.jdbcUtil.throwError(Unknown Source)
at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
at net.sf.hibernate.impl.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:22)
at net.sf.hibernate.collection.AbstractCollectionPersister.deleteRows(AbstractCollectionPersister.java:566)
at net.sf.hibernate.impl.ScheduledCollectionUpdate.execute(ScheduledCollectionUpdate.java:47)
at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java:2414)
at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java:2370)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2236)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
at BusinessDelegate.saveAll(BusinessDelegate.java:118)
at SaveAction.actionPerformed(SaveAction.java:32)
That happens when I am updating (updateOrSave) an existing DefaultEntry object (implements IFEntry):
Code:
DefaultEntry@107bd0d[
work=<null>,
timestamp=2004-07-16 21:18:43.845,
id=121,
children=[],
parents=[
EntryConnector@13dd8[
rank=0,
EntryConnectorId@18353cf[
parent=188,
child=121
],
timestamp=2004-07-16 23:39:05.745
]
],
]
which has a relation to the EntryConnector (-> ENTRY_ENTRY table). Hibernate tries to update this dependent object too and wants to set one part of the primary key (CHILD_ID -> child=121) of ENTRY_ENTRY to null, which invokes the exception. I don't see why this is necessary, it should only update the DefaultEntry, not the EntryConnector. And if it updates the EntryConnector, then why setting this half-PK to null? Can I configure it to not do that?
I've got a simple two-tables ERD, representing a graph.
Code:
CREATE TABLE ENTRY (
ID IDENTITY,
TIMESTAMP TIMESTAMP,
...
);
CREATE TABLE ENTRY_ENTRY (
PARENT_ID INTEGER,
CHILD_ID INTEGER,
TIMESTAMP TIMESTAMP,
RANK INTEGER,
PRIMARY KEY (PARENT_ID, CHILD_ID)
);
and the row being updated is of the ENTRY table. I debuged throu the process and the ENTRY row is updated ok, but the dependent collection fails (the ENTRYs are dependent on the connection table ENTRY_ENTRY). Here's the config:
Code:
<hibernate-mapping package="">
<class name="IFEntry" table="ENTRY">
<id name="id" type="integer" unsaved-value="null">
<generator class="identity"/>
</id>
<discriminator column="discriminator"/>
<timestamp name="timestamp"/>
...
<set name="children" table="ENTRY_ENTRY" inverse="true">
<key column="PARENT_ID"/>
<one-to-many class="EntryConnector"/>
</set>
<set name="parents" table="ENTRY_ENTRY">
<key column="CHILD_ID"/>
<one-to-many class="EntryConnector"/>
</set>
<subclass name="DefaultEntry" discriminator-value="default"/>
</class>
</hibernate-mapping>
<hibernate-mapping package="">
<class name="EntryConnector" table="ENTRY_ENTRY">
<composite-id name="id" class="EntryConnectorId">
<key-many-to-one name="parent" class="IFEntry" column="parent_id"/>
<key-many-to-one name="child" class="IFEntry" column="child_id"/>
</composite-id>
<timestamp name="timestamp"/>
<property name="rank" type="integer"/>
</class>
</hibernate-mapping>
I am using HSQLDB 1.7.2rc6 and my code in the session (B) is:
Code:
public static void saveAll() {
try {
final Session session = HibernateUtils.currentSession();
final Transaction tx = session.beginTransaction();
for (Iterator iterator = cacheToDelete.iterator(); iterator.hasNext();) {
final Object o = iterator.next();
System.out.println("deleting: " + o);
session.delete(o);
}
for (Iterator iterator = cacheToSave.iterator(); iterator.hasNext();) {
final Object o = iterator.next();
System.out.println("saving : " + o);
session.saveOrUpdate(o);
}
tx.commit();
HibernateUtils.closeSession();
}
catch (HibernateException e) {
e.printStackTrace();
throw new IllegalStateException("Error during save");
}
}
Thanks for any idea or advice.
Daniel Frey