Hi,
currently I have problem with a mapping and self-reference. My User class is mapped to two tables, so I used the entity-name feature. The first entity is for the current version, the other ist to keep historical data. Here's my class (stripped down):
Code:
public class User {
private Long id;
private Long historyId;
private User modifiedBy;
private Date modified;
private Date effectiveStartDate;
private Date effectiveEndDate;
private User owner;
private String loginName;
private String loginPass;
private Integer version;
private List acls;
...
getters + setters ...
}
Everything works fine (updates to CurrentUser, inserts to HistoricalUser) until I have a self-reference in 'modifiedBy' (or 'owner') which holds the reference to the user who made the last changes to the record. Hibernate 'thinks' it should make a reference to the entity 'HistoricalUser', although I specified it by entity-name=CurrentUser. This leads to an exception where the DB complains about a foreign-key violation.
My question: How do I tell hibernate to use the correct entity?
Hibernate version: 3.2.0ga
Mapping documents:Code:
...
<hibernate-mapping package="model">
<class name="User" table="users" entity-name="CurrentUser">
<id unsaved-value="null" name="id" column="id" type="java.lang.Long">
<generator class="native"></generator>
</id>
<version name="version" column="version" type="int" unsaved-value="null" />
<property name="loginName" type="java.lang.String" column="loginName" unique="true" not-null="true" length="255"></property>
<property name="loginPass" type="java.lang.String" column="loginPass" not-null="true" length="255"></property>
<many-to-one name="parent" entity-name="CurrentUser" column="parent"></many-to-one>
<list name="acls" table="acls" lazy="false">
<key column="userid"/>
<list-index column="sort"/>
<composite-element class="Acl">
<property name="path" type="string" length="255" not-null="true" />
<property name="rights" type="integer" not-null="true" />
</composite-element>
</list>
<property column="modified" name="modified" type="timestamp" not-null="true"/>
<many-to-one name="owner" class="User" entity-name="CurrentUser" column="ownerid"></many-to-one>
</class>
<class name="User" table="usersHistory" entity-name="HistoricalUser" >
<id unsaved-value="null" name="historyId" column="id" type="java.lang.Long" access="property">
<generator class="native"></generator>
</id>
<property name="effectiveStartDate" type="timestamp" column="startDate" />
<property name="effectiveEndDate" type="timestamp" column="endDate" />
<property name="modified" type="timestamp" column="endDate" update="false" insert="false" />
<property name="version" type="integer" column="version" not-null="true" />
<property name="id" type="long" column="masterId" not-null="true"/>
<property name="active" type="boolean" column="active" not-null="true" />
<many-to-one name="owner" column="ownerId" class="User" entity-name="CurrentUser"></many-to-one>
<many-to-one name="modifiedBy" class="User" entity-name="CurrentUser" column="modifiedby"></many-to-one>
<property name="loginName" type="java.lang.String" column="loginName" not-null="true" length="255"></property>
<property name="loginPass" type="java.lang.String" column="loginPass" not-null="true" length="255"></property>
<list name="acls" table="aclsHistory" lazy="false" >
<key column="historyid"/>
<list-index column="sort"/>
<composite-element class="Acl" >
<property name="path" type="string" length="255" not-null="true" />
<property name="rights" type="integer" not-null="true" />
</composite-element>
</list>
</class>
<query name="updateHistoricalUser">UPDATE HistoricalUser SET endDate = :now WHERE masterId = :targetId AND endDate IS NULL</query>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():Code:
// already saved ? sohObject is user
boolean updateHist = (sohObject.getId() != null);
// save user
Date now = new Date();
sohObject.setModified(now);
sohObject.setEffectiveStartDate(now);
sohObject.setHistoryId(null);
session.saveOrUpdate(getCurrentEntityName(),sohObject);
session.flush();
if (updateHist) {
Query q = session.getNamedQuery("update" + getHistoricalEntityName());
q.setTimestamp("now", now);
q.setLong("targetId", sohObject.getId().longValue());
q.executeUpdate();
}
session.flush();
// System.out.println(session.getStatistics().getEntityKeys());
session.evict(sohObject);
// new history entry
session.merge(getHistoricalEntityName(), sohObject);
Full stack trace of any exception that occurs:Name and version of the database you are using:MySQL 4.1.21
The generated SQL (show_sql=true):see below
Debug level Hibernate log excerpt:Code:
2006-10-23 16:28:37,640 DEBUG [org.hibernate.SQL] insert into usersHistory (startDate, endDate, version, masterId, active, ownerId, primaryGroupId, secondaryGroupId, modifiedby, loginName, loginPass, parentid) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2006-10-23 16:28:37,640 DEBUG [org.hibernate.jdbc.AbstractBatcher] preparing statement
2006-10-23 16:28:37,640 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] Dehydrating entity: [HistoricalUser#<null>]
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.TimestampType] binding '2006-10-23 16:28:37' to parameter: 1
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.TimestampType] binding null to parameter: 2
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.IntegerType] binding '1' to parameter: 3
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.LongType] binding '5' to parameter: 4
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.BooleanType] binding 'true' to parameter: 5
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.LongType] binding null to parameter: 6
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.LongType] binding null to parameter: 7
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.LongType] binding null to parameter: 8
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.LongType] binding null to parameter: 9
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.StringType] binding 'testuserServ1161613717593' to parameter: 10
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.StringType] binding 'pass' to parameter: 11
2006-10-23 16:28:37,640 DEBUG [org.hibernate.type.LongType] binding null to parameter: 12
2006-10-23 16:28:37,656 DEBUG [org.hibernate.id.IdentifierGeneratorFactory] Natively generated identity: 6
2006-10-23 16:28:37,656 DEBUG [org.hibernate.jdbc.AbstractBatcher] about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
2006-10-23 16:28:37,656 DEBUG [org.hibernate.jdbc.AbstractBatcher] closing statement
2006-10-23 16:28:37,656 DEBUG [org.hibernate.transaction.JDBCTransaction] commit
2006-10-23 16:28:37,656 DEBUG [org.hibernate.impl.SessionImpl] automatically flushing session
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] flushing session
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] processing flush-time cascades
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] dirty checking collections
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] Flushing entities and processing referenced collections
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.WrapVisitor] Wrapped collection in role: HistoricalUser.acls
2006-10-23 16:28:37,656 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] HistoricalUser.owner is dirty
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.DefaultFlushEntityEventListener] Updating entity: [HistoricalUser#6]
2006-10-23 16:28:37,656 DEBUG [org.hibernate.engine.Collections] Collection found: [HistoricalUser.acls#6], was: [<unreferenced>] (initialized)
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] Processing unreferenced collections
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] Scheduling collection removes/(re)creates/updates
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] Flushed: 1 (re)creations, 0 updates, 0 removals to 1 collections
2006-10-23 16:28:37,656 DEBUG [org.hibernate.pretty.Printer] listing entities:
2006-10-23 16:28:37,656 DEBUG [org.hibernate.pretty.Printer] User
2006-10-23 16:28:37,656 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] executing flush
2006-10-23 16:28:37,656 DEBUG [org.hibernate.jdbc.ConnectionManager] registering flush begin
2006-10-23 16:28:37,656 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] Updating entity: [HistoricalUser#6]
2006-10-23 16:28:37,656 DEBUG [org.hibernate.jdbc.AbstractBatcher] about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
2006-10-23 16:28:37,656 DEBUG [org.hibernate.SQL] update usersHistory set startDate=?, endDate=?, version=?, masterId=?, active=?, ownerId=?, primaryGroupId=?, secondaryGroupId=?, modifiedby=?, loginName=?, loginPass=?, parentid=? where id=?
2006-10-23 16:28:37,656 DEBUG [org.hibernate.jdbc.AbstractBatcher] preparing statement
2006-10-23 16:28:37,656 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] Dehydrating entity: [HistoricalUser#6]
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.TimestampType] binding '2006-10-23 16:28:37' to parameter: 1
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.TimestampType] binding null to parameter: 2
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.IntegerType] binding '1' to parameter: 3
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.LongType] binding '5' to parameter: 4
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.BooleanType] binding 'true' to parameter: 5
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.LongType] binding '6' to parameter: 6
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.LongType] binding null to parameter: 7
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.LongType] binding null to parameter: 8
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.LongType] binding null to parameter: 9
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.StringType] binding 'testuserServ1161613717593' to parameter: 10
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.StringType] binding 'pass' to parameter: 11
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.LongType] binding null to parameter: 12
------- this seems to be wrong ----
2006-10-23 16:28:37,656 DEBUG [org.hibernate.type.LongType] binding '6' to parameter: 13
2006-10-23 16:28:37,656 DEBUG [org.hibernate.jdbc.AbstractBatcher] Executing batch size: 1
2006-10-23 16:28:37,671 DEBUG [org.hibernate.jdbc.AbstractBatcher] about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
2006-10-23 16:28:37,671 DEBUG [org.hibernate.jdbc.AbstractBatcher] closing statement
2006-10-23 16:28:37,671 DEBUG [org.hibernate.util.JDBCExceptionReporter] Could not execute JDBC batch update [update usersHistory set startDate=?, endDate=?, version=?, masterId=?, active=?, ownerId=?, primaryGroupId=?, secondaryGroupId=?, modifiedby=?, loginName=?, loginPass=?, parentid=? where id=?]
java.sql.BatchUpdateException: Cannot add or update a child row: a foreign key constraint fails
at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:647)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:584)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:500)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:473)
at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(TransactionAspectSupport.java:267)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)
at $Proxy3.saveUser(Unknown Source)
at UserTest.testUserService(UserTest.java:129)
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:324)
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.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
2006-10-23 16:28:37,671 WARN [org.hibernate.util.JDBCExceptionReporter] SQL Error: 1216, SQLState: 23000
2006-10-23 16:28:37,671 ERROR [org.hibernate.util.JDBCExceptionReporter] Cannot add or update a child row: a foreign key constraint fails
2006-10-23 16:28:37,671 ERROR [org.hibernate.event.def.AbstractFlushingEventListener] Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:249)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:584)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:500)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:473)
at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(TransactionAspectSupport.java:267)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)
at $Proxy3.saveUser(Unknown Source)
at UserTest.testUserService(UserTest.java:129)
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:324)
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.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.sql.BatchUpdateException: Cannot add or update a child row: a foreign key constraint fails
at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:647)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
... 34 more