Hello,
I have a problem using two One-to-Many relations.
I have three pojos:
------ ------------- --------
|User| <-- |UserGroup| --> |Group|
------- -------------- --------
| |
Set One-to-Many_UserGroup |
set One-to-Many_UserGroup
each pojo User and Group have a One-to-Many relationships to UserGroup, I know this is equivalente to having a Many-to-Many relationship between User and Group, but i need it as two One-to-Many associations.
I have cascading set to all in the associations as demonstrated in the hbm's that will follow.
The problem is that:
- when i remove a user through cascading the entrys in UserGroup table will be removed. Hibernate will force a flush in the session and try to update the associations from the Pojo Group. I have set inverse="true" in the association to prevent Hibernate from trying to re-save the object that was deleted.
When hibernate is trying to update the Group Pojo association the folowing exception is thrown:
org.springframework.orm.hibernate.HibernateObjectRetrievalFailureException: deleted object would be re-saved by cascade (remove deleted object from associations): 5, of class: pt.gedi.siag.persistence.ASUtilizadorGrupo;
nested exception is net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 5, of class: pt.gedi.siag.persistence.ASUtilizadorGrupo
net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 5, of class: pt.gedi.siag.persistence.ASUtilizadorGrupo
at net.sf.hibernate.impl.SessionImpl.forceFlush(SessionImpl.java:753)
at net.sf.hibernate.impl.SessionImpl.save(SessionImpl.java:731)
at net.sf.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:1377)
at net.sf.hibernate.engine.Cascades$4.cascade(Cascades.java:114)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:436)
at net.sf.hibernate.engine.Cascades.cascadeCollection(Cascades.java:526)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:452)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:503)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:482)
at net.sf.hibernate.impl.SessionImpl.preFlushEntities(SessionImpl.java:2674)
at net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2251)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2240)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
at org.springframework.orm.hibernate.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:472)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:377)
at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(TransactionAspectSupport.java:243)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:66)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:144)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:632)
at pt.gedi.siag.services.us.adminsistema.UsersServiceImpl$$EnhancerByCGLIB$$2bad1f3f.removeEntry(<generated>)
at pt.gedi.siag.services.us.adminsistema.test.UsersServiceTestCase.testRemoveUser(UsersServiceTestCase.java:69)
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 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:421)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:305)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:186)
Because of my suspition that it could be a BUG, i tried to do another thing,
having the Group Pojo association One-To-Many_UserGroup attribute inverse="true", i tried:
groupPojo.getOneToManyUserGroupCollection().add( newUserGroupPojo );
and for my surprise this inserted in the UserGroup table the new data from the newUserGroupPojo.
For what i have read if the inverse attribute was false this behaviour was expected, for inverse="true" the data from the collection should'nt persisted to the Table.
Is this a bug, or do i have something wrong?
In my tests i have tried to put lazy="true" in the association and then ran the tests that passed but only because i didnt use the One-to-Many association from Group Pojo. When I used it the test throwed the same exception.
This proves that he tries to re-save the object from the One-to-Many collection in Group Pojo if it is loaded.
But with inverse="true" this shouldnt happen.
Anexed to the document follows my original hbm and pojos.
In the article "Inside explanation of inverse=true" in
http://www.hibernate.org/155.html,
For Update = "false"
Code:
Parent p = (Parent) session.load(Parent.class, parentId);
Parent p2 = (Parent) session.load(Parent.class, parentId2);
c = (Child) session.find(
"from Child as child where child.parent = ?",
p, Hibernate.entity(Parent.class)).get(0);
p.getChildren().remove(c);
p2.getChildren().add(p);
Will do the following SQL queries
Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=? //get parent 1
Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=? //get parent 2
Hibernate: select child0_.id as id, child0_.parent_id as parent_id from child child0_ where (child0_.parent_id=? ) //get children
Hibernate: select child0_.id as id__, child0_.id as id, child0_.parent_id as parent_id from child child0_ where child0_.parent_id=?
Hibernate: select child0_.id as id__, child0_.id as id, child0_.parent_id as parent_id from child child0_ where child0_.parent_id=?
//load childrens of Parent 1 and 2 (can't avoid this see FAQ)
Relationship update is not executed because update is only done on the parent side.
My Settings:
(For the saque of this example i called the pojos User, UserGroup and Group, but the real names are ASUtilizador, ASUtilizadorGrupo and ASGrupo)
User.hbm.xml:
.....
<set
name="relOM_ASUtilizador_ASUtilizadorGrupo"
inverse="true"
cascade="all-delete-orphan"
lazy="false">
<key column="ChvEASUtilizador"/>
<one-to-many class="pt.gedi.siag.persistence.ASUtilizadorGrupo"/>
</set>
...
Group.hbm.xml:
...
<set
name="relOM_ASGrupo_ASUtilizadorGrupo"
inverse="true"
cascade="all">
<key column="ChvEASGrupo"/>
<one-to-many class="pt.gedi.siag.persistence.ASUtilizadorGrupo"/>
</set>
....
UserGroup.hbm.xml
...
<many-to-one
name="relMO_ASUtilizadorGrupo_ASGrupo"
class="pt.gedi.siag.persistence.ASGrupo"
column="ChvEASGrupo"
/>
<many-to-one
name="relMO_ASUtilizadorGrupo_ASUtilizador"
class="pt.gedi.siag.persistence.ASUtilizador"
column="ChvEASUtilizador"
/>
...
Hibernate version:2
Full stack trace of any exception that occurs:
org.springframework.orm.hibernate.HibernateObjectRetrievalFailureException: deleted object would be re-saved by cascade (remove deleted object from associations): 5, of class: pt.gedi.siag.persistence.ASUtilizadorGrupo;
nested exception is net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 5, of class: pt.gedi.siag.persistence.ASUtilizadorGrupo
net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 5, of class: pt.gedi.siag.persistence.ASUtilizadorGrupo
at net.sf.hibernate.impl.SessionImpl.forceFlush(SessionImpl.java:753)
at net.sf.hibernate.impl.SessionImpl.save(SessionImpl.java:731)
at net.sf.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:1377)
at net.sf.hibernate.engine.Cascades$4.cascade(Cascades.java:114)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:436)
at net.sf.hibernate.engine.Cascades.cascadeCollection(Cascades.java:526)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:452)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:503)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:482)
at net.sf.hibernate.impl.SessionImpl.preFlushEntities(SessionImpl.java:2674)
at net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2251)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2240)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
at org.springframework.orm.hibernate.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:472)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:377)
at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(TransactionAspectSupport.java:243)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:66)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:144)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:632)
at pt.gedi.siag.services.us.adminsistema.UsersServiceImpl$$EnhancerByCGLIB$$2bad1f3f.removeEntry(<generated>)
at pt.gedi.siag.services.us.adminsistema.test.UsersServiceTestCase.testRemoveUser(UsersServiceTestCase.java:69)
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 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:421)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:305)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:186)
Name and version of the database you are using:mySQL Dist 3.2