When using
cascade="save-update" in a many-to-one relation, I get different results with
transient and
detached objects. In this case the table permission has a many-to-one relation to the table operation.
I'm having a permission object in detached state. This object is modified by calling the setOperation method of permission (many-to-one relation). This changed permission object is re-attached in a new session by calling the hibernate update method and then commited. There is a different behavior when calling the setOperation method using a transient or a detached operation object.
- Using a transient operation object the update method of the session (for the permission object) works correctly as expected. The transient operation is created through the cascade="save-update" option and the relation is set.
- Using a detached operation object the update method of the session (for the permission object) results in the attached exception.
I expected that using a detached object instead of a transient object should make no difference for cascade="save-update". Can anybody explain this strange behavior?
Thanks.
Andreas
Hibernate version: 3.1.3
Mapping documents:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="de.ike.sinter.rbac.hibernate">
<class name="Permission" table="rbac_permission" select-before-update="true">
<meta attribute="sync-DAO">false</meta>
<meta attribute="implements">Comparable</meta>
<id name="Id" type="string" column="id">
<generator class="org.hibernate.id.UUIDHexGenerator"/>
</id>
<version name="Version" column="version" type="integer"/>
<property name="LastUpdate" column="last_update" type="timestamp" not-null="false"/>
<many-to-one name="Operation" column="operation_id" class="de.ike.sinter.rbac.hibernate.Operation" lazy="false" not-null="true" cascade="save-update"/>
<many-to-one name="ResourceObject" column="resource_object_id" class="de.ike.sinter.rbac.hibernate.ResourceObject" lazy="false" not-null="true" cascade="save-update"/>
<set name="Roles" table="rbac_nm_role_permission">
<key column="permission_id" not-null="true"/>
<many-to-many column="role_id" class="de.ike.sinter.rbac.hibernate.Role"/>
</set>
</class>
</hibernate-mapping>
and
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="de.ike.sinter.rbac.hibernate">
<class name="Operation" table="rbac_operation" select-before-update="true">
<meta attribute="sync-DAO">false</meta>
<meta attribute="implements">Comparable</meta>
<id name="Id" type="string" column="id">
<generator class="org.hibernate.id.UUIDHexGenerator"/>
</id>
<version name="Version" column="version" type="integer"/>
<property name="Method" column="method" type="string" not-null="true" length="255" unique="true"/>
<property name="LastUpdate" column="last_update" type="timestamp" not-null="false"/>
<many-to-one name="ResourceType" column="resource_type_id" class="de.ike.sinter.rbac.hibernate.ResourceType" not-null="true"/>
<many-to-one name="OperationType" column="operation_type_id" class="de.ike.sinter.rbac.hibernate.OperationType" not-null="true"/>
<set name="Permissions" inverse="false">
<key column="operation_id"/>
<one-to-many class="de.ike.sinter.rbac.hibernate.Permission"/>
</set>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():Code:
org.hibernate.Session sess = hibernateSfMgr.createNewSession(DB_NAME);
org.hibernate.Transaction tx = null;
try {
tx = sess.beginTransaction();
sess.update(permission);
tx.commit();
} catch (org.hibernate.StaleObjectStateException e) {
if (tx != null)
tx.rollback();
throw e;
} catch (RuntimeException e) {
if (tx != null)
tx.rollback();
throw e;
} finally {
if (sess != null && sess.isOpen())
sess.close();
}
Full stack trace of any exception that occurs:Code:
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:202)
at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:91)
at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:86)
at org.hibernate.jdbc.AbstractBatcher.prepareBatchStatement(AbstractBatcher.java:171)
at org.hibernate.persister.collection.AbstractCollectionPersister.remove(AbstractCollectionPersister.java:965)
at org.hibernate.action.CollectionRemoveAction.execute(CollectionRemoveAction.java:28)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:141)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at de.ike.sinter.rbac.DefaultRbacManager.updatePermission(DefaultRbacManager.java:2275)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.apache.avalon.excalibur.component.ComponentProxyGenerator$ComponentInvocationHandler.invoke(ComponentProxyGenerator.java:143)
at $Proxy0.updatePermission(Unknown Source)
at de.ike.sinter.rbac.RbacPermissionTestCase.updatePermission(RbacPermissionTestCase.java:994)
at de.ike.sinter.rbac.RbacPermissionTestCase.testPermissionAsObject(RbacPermissionTestCase.java:167)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.sql.BatchUpdateException: Data truncation: Column set to default value; NULL supplied to NOT NULL column 'operation_id' at row 1
at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:657)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195)
... 37 more
Name and version of the database you are using: MySQL 5.0.19
The generated SQL (show_sql=true):Code:
Hibernate: select permission_.id, permission_.version as version11_, permission_.last_update as last3_11_, permission_.operation_id as operation4_11_, permission_.resource_object_id as resource5_11_ from rbac_permission permission_ where permission_.id=?
Hibernate: select operation_.id, operation_.version as version9_, operation_.method as method9_, operation_.last_update as last4_9_, operation_.resource_type_id as resource5_9_, operation_.operation_type_id as operation6_9_ from rbac_operation operation_ where operation_.id=?
Hibernate: select resourceob_.id, resourceob_.version as version13_, resourceob_.specific_part as specific3_13_, resourceob_.data as data13_, resourceob_.last_update as last5_13_, resourceob_.resource_type_id as resource6_13_, resourceob_.workpackage_id as workpack7_13_, resourceob_.current_task_id as current8_13_ from rbac_resource_object resourceob_ where resourceob_.id=?
Hibernate: update rbac_permission set version=?, last_update=?, operation_id=?, resource_object_id=? where id=? and version=?
Hibernate: update rbac_operation set version=?, method=?, last_update=?, resource_type_id=?, operation_type_id=? where id=? and version=?
Hibernate: update rbac_resource_object set version=?, specific_part=?, data=?, last_update=?, resource_type_id=?, workpackage_id=?, current_task_id=? where id=? and version=?
Hibernate: update rbac_permission set operation_id=null where operation_id=?
Debug level Hibernate log excerpt:Code:
WARN [main] (JDBCExceptionReporter.java:71) - SQL Error: 0, SQLState: 01004
ERROR [main] (JDBCExceptionReporter.java:72) - Data truncation: Column set to default value; NULL supplied to NOT NULL column 'operation_id' at row 1
ERROR [main] (AbstractFlushingEventListener.java:300) - Could not synchronize database state with session