I am currently having an issue with deleting an object after an update, where the parent object contains a Map and cascade is set to all-delete-orphan.
I apologise for the long post, but I can't explain it very well if it's short! Basically, I can't work out if what I'm doing is completely wrong, or if there is something funky going on with the cascade. A simple (as simple as it gets...) working example is available if required.
The environment setup is as follows:
Hibernate 2.1.4 (or 2.1.3)
MySQL 4.0.18, or Oracle 9.2.0.4
Latest available JDBC drivers for both DBs.
Simple Scenario:
A Project class contains a Set of possible Roles (unidirectional) for the entire Project, and a Map of Users(key) with UserDetails(value) for the Project. The UserDetails has a set of Roles (unidirectional) allocated to the User for the project.
I've narrowed the issue down to occur under the following circumstances (
at the beginning of each step a new session is opened, at the end of each the session is flushed and closed):
1) Insert a User.
2) Create a project with two roles. Add the pre-persisted User, with new UserDetails mapping to "Role One". Insert Project.
3) Update the project - remove "User One" (and associated UserDetails). Update the Project (UserDetails correctly deleted).
4) Delete the project - ERROR - attempts to remove the UserDetails for "User One" again, fails with "Could not synchronize database state with session", since it has already been deleted.
Exception (MySQL for now, although I can obtain the Oracle one tomorrow morning):
Code:
Hibernate: delete from PROJECT_USER where PROJECT_USER_ID=?
00:41:34,540 DEBUG IntegerType:46 - binding '24' to parameter: 1
00:41:34,540 ERROR SessionImpl:2375 - Could not synchronize database state with session
net.sf.hibernate.HibernateException: Batch update row count wrong: 0
at net.sf.hibernate.impl.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:65)
at net.sf.hibernate.impl.BatcherImpl.executeBatch(BatcherImpl.java:122)
at net.sf.hibernate.impl.BatcherImpl.prepareStatement(BatcherImpl.java:59)
at net.sf.hibernate.impl.BatcherImpl.prepareStatement(BatcherImpl.java:56)
at net.sf.hibernate.impl.BatcherImpl.prepareBatchStatement(BatcherImpl.java:109)
at net.sf.hibernate.persister.EntityPersister.delete(EntityPersister.java:582)
at net.sf.hibernate.impl.ScheduledDeletion.execute(ScheduledDeletion.java:29)
at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java:2414)
at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java:2372)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2236)
at com.tangible.test.dao.BaseDAO.delete(BaseDAO.java:54)
The error doesn't occur if there is only one role associated with the project. I have checked the Hibernate LOG4J output on full debug - the correct SQL is being called and committed, and the entity listings are as follows:
Before update:Code:
01:01:26,540 DEBUG Printer:75 - listing entities:
01:01:26,540 DEBUG Printer:82 - com.tangible.test.bo.Project{userDetails=[], roles=[Role#27, Role#28], name=NEW_PROJECT_NAME, id=14}
01:01:26,540 DEBUG Printer:82 - com.tangible.test.bo.User{name=USER_NAME|1, id=40}
01:01:26,540 DEBUG Printer:82 - com.tangible.test.bo.Role{name=ROLE_2, id=27}
01:01:26,540 DEBUG Printer:82 - com.tangible.test.bo.UserDetails{adminUser=false, roles=[Role#27, Role#28], lockedForProject=true, id=25}
01:01:26,540 DEBUG Printer:82 - com.tangible.test.bo.Role{name=ROLE_1, id=28}
After update:Code:
01:01:26,603 DEBUG Printer:75 - listing entities:
01:01:26,618 DEBUG Printer:82 - com.tangible.test.bo.Project{userDetails=[], roles=[Role#27, Role#28], name=NEW_PROJECT_NAME, id=14}
01:01:26,618 DEBUG Printer:82 - com.tangible.test.bo.User{name=USER_NAME|1, id=40}
01:01:26,618 DEBUG Printer:82 - com.tangible.test.bo.Role{name=ROLE_2, id=27}
01:01:26,618 DEBUG Printer:82 - com.tangible.test.bo.Role{name=ROLE_1, id=28}
When it cascades through the Project object for final deletion (UserDetails object reappears):Code:
01:01:26,650 DEBUG Printer:75 - listing entities:
01:01:26,650 DEBUG Printer:82 - com.tangible.test.bo.Project{userDetails=[], roles=[Role#27, Role#28], name=NEW_PROJECT_NAME, id=14}
01:01:26,650 DEBUG Printer:82 - com.tangible.test.bo.Role{name=ROLE_2, id=27}
01:01:26,650 DEBUG Printer:82 - com.tangible.test.bo.UserDetails{adminUser=false, roles=[Role#27, Role#28], lockedForProject=true, id=25}
01:01:26,650 DEBUG Printer:82 - com.tangible.test.bo.Role{name=ROLE_1, id=28}
Mapping Files:Project Mapping File
Code:
<hibernate-mapping>
<class name="com.tangible.test.bo.Project" table="PROJECT" dynamic-update="false" dynamic-insert="false">
<id name="id" column="PROJECT_ID" type="int" unsaved-value="0">
<generator class="native"/>
</id>
<property name="name" type="string" update="true" insert="true" access="property" column="PROJECT_NAME" length="25"/>
<map name="userDetails" lazy="false" sort="unsorted" inverse="false" cascade="all-delete-orphan">
<key column="PROJECT_ID"/>
<index-many-to-many class="com.tangible.test.bo.User" column="USER_ID"/>
<one-to-many class="com.tangible.test.bo.UserDetails"/>
</map>
<set name="roles" lazy="false" inverse="false" cascade="all-delete-orphan" sort="unsorted" >
<key column="PROJECT_ID" />
<one-to-many class="com.tangible.test.bo.Role" />
</set>
</class>
</hibernate-mapping>
UserDetails Mapping File
Code:
<hibernate-mapping>
<class name="com.tangible.test.bo.UserDetails" table="PROJECT_USER" dynamic-update="false" dynamic-insert="false">
<id name="id" column="PROJECT_USER_ID" type="int" unsaved-value="0">
<generator class="native"/>
</id>
<property name="adminUser" type="boolean" update="true" insert="true" access="property" column="ADMIN_USER"/>
<property name="lockedForProject" type="boolean" update="true" insert="true" access="property" column="LOCKED_FOR_PROJECT"/>
<set name="roles" table="PROJECT_USER_ROLES" lazy="false" inverse="false" cascade="none" sort="unsorted">
<key column="PROJECT_USER_ID"/>
<many-to-many class="com.tangible.test.bo.Role" column="ROLE_ID" outer-join="auto"/>
</set>
</class>
</hibernate-mapping>
User Mapping File
Code:
<hibernate-mapping>
<class name="com.tangible.test.bo.User" table="TANGIBLE_USER" dynamic-update="false" dynamic-insert="false">
<id name="id" column="USER_ID" type="int" unsaved-value="0">
<generator class="native"/>
</id>
<property name="name" type="string" update="true" insert="true" access="property" column="USERNAME" length="25"/>
</class>
</hibernate-mapping>
Role Mapping File
Code:
<hibernate-mapping>
<class name="com.tangible.test.bo.Role" table="TANGIBLE_ROLE" dynamic-update="false" dynamic-insert="false">
<id name="id" column="ROLE_ID" type="int" unsaved-value="0">
<generator class="native"/>
</id>
<property name="name" type="string" update="true" insert="true" access="property" column="ROLE_NAME" length="25"/>
</class>
</hibernate-mapping>
Java Code for Updating:Code:
Session session = SessionHandler.getCurrentSession();
Transaction trans = session.beginTransaction();
session.saveOrUpdateCopy(updateObject);
session.flush();
trans.commit();
Java Code for Deleting:Code:
Session session = SessionHandler.getCurrentSession();
Transaction trans = session.beginTransaction();
session.delete(deleteObject);
session.flush();
trans.commit();
Any help or advice is much appreciated.