-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: 2x many-to-one = many-to-many .. then delete that
PostPosted: Fri Nov 17, 2006 9:51 am 
Newbie

Joined: Fri Nov 17, 2006 9:40 am
Posts: 11
Location: Switzerland
Hi

We have several many-to-many relationships, but with additional data on the mapping table. For whatever (historical) reasons, our many-to-many relationships are mapped as 2 one-to-may like this:

Code:
<hibernate-mapping>
    <class name="net.umbrella.data.model.ParticipantPriceitemMapModel" table="PARTICIPANT_PRICEITEM_MAP">
        <composite-id name="id" class="net.umbrella.data.model.ParticipantPriceitemMapIdModel">
            <key-property name="participantid" type="int">
                <column name="PARTICIPANTID" />
            </key-property>
            <key-property name="priceitemid" type="int">
                <column name="PRICEITEMID" />
            </key-property>
        </composite-id>
        <many-to-one name="participants" class="net.umbrella.data.model.ParticipantModel" update="false" insert="false" fetch="select" cascade="persist,save-update">
            <column name="PARTICIPANTID" not-null="true" />
        </many-to-one>
        <many-to-one name="priceitems" class="net.umbrella.data.model.PriceitemModel" update="false" insert="false" fetch="select" cascade="persist,save-update">
            <column name="PRICEITEMID" not-null="true" />
        </many-to-one>
    </class>
</hibernate-mapping>


You can see that this Map-Table contains references to a PARTICIPANT and to a PRICEITEM. I want to delete the PARTICIPANT, but leave the PRICEITEM intact.
Without cascading, Hibernate will tell me that the deleted ParticipantPriceitemMapModel will be re-saved. I guess that's because the PRICEITEM still holds a valid reference to the entity in its getParticipantPriceitemMaps() collection.

So I added an Interceptor which "cleans" up map entries before deleting them:

Code:
public void onDelete(Object entity, Serializable id,
                     Object[] state, String[] propertyNames, Type[] types){
      if ( entity instanceof ParticipantPriceitemMapModel) {
         ParticipantPriceitemMapModel map = (ParticipantPriceitemMapModel) entity;
         map.getParticipants().getParticipantPriceitemMaps().remove(entity);
         map.getPriceitems().getParticipantPriceitemMaps().remove(entity);
      }
}


Now I get a java.util.ConcurrentModificationException in cascadeCollection. I guess I'm removing stuff whie cascading, not a good idea? But then, how do I do this?

Thanks
Simon


Top
 Profile  
 
 Post subject:
PostPosted: Fri Nov 17, 2006 9:55 am 
Newbie

Joined: Fri Nov 17, 2006 9:40 am
Posts: 11
Location: Switzerland
Using Hibernate 3.1.3

I note that my Interceptor doesn't show up in the stack trace:

Code:
java.util.ConcurrentModificationException
   at java.util.HashMap$HashIterator.nextEntry(HashMap.java:787)
   at java.util.HashMap$KeyIterator.next(HashMap.java:823)
   at org.hibernate.collection.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:555)
   at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:290)
   at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:185)
   at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:160)
   at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:108)
   at org.hibernate.engine.Cascade.cascade(Cascade.java:248)
   at org.hibernate.engine.Cascade.cascade(Cascade.java:223)
   at org.hibernate.event.def.DefaultDeleteEventListener.cascadeBeforeDelete(DefaultDeleteEventListener.java:220)
   at org.hibernate.event.def.DefaultDeleteEventListener.deleteEntity(DefaultDeleteEventListener.java:169)
   at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:110)
   at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:761)
   at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:753)
   at org.hibernate.engine.CascadingAction$2.cascade(CascadingAction.java:47)
   at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:213)
   at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:157)
   at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:108)
   at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:290)
   at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:185)
   at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:160)
   at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:108)
   at org.hibernate.engine.Cascade.cascade(Cascade.java:248)
   at org.hibernate.engine.Cascade.cascade(Cascade.java:223)
   at org.hibernate.event.def.DefaultDeleteEventListener.cascadeBeforeDelete(DefaultDeleteEventListener.java:220)
   at org.hibernate.event.def.DefaultDeleteEventListener.deleteEntity(DefaultDeleteEventListener.java:169)
   at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:110)
   at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:761)
   at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:753)
   at org.hibernate.engine.Cascade.deleteOrphans(Cascade.java:351)
   at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:318)
   at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:185)
   at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:160)
   at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:108)
   at org.hibernate.engine.Cascade.cascade(Cascade.java:248)
   at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:130)
   at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:121)
   at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
   at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
   at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
   at net.umbrella.data.dao.BaseDaoImpl.save(BaseDaoImpl.java:51)
   at net.umbrella.data.OrderServiceImpl.save(OrderServiceImpl.java:80)
   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.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:203)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:162)
   at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:107)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
   at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:209)
   at $Proxy19.save(Unknown Source)
   at net.umbrella.domainmodel.Order.saveCurrent(Order.java:761)
   at net.umbrella.domainmodel.B2bMir.populateAndSaveOrder(B2bMir.java:591)
   at net.umbrella.test.TestTestCases.testTestCase1(TestTestCases.java:154)
   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 org.springframework.test.ConditionalTestCase.runBare(ConditionalTestCase.java:69)
   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)



Top
 Profile  
 
 Post subject:
PostPosted: Fri Nov 17, 2006 10:12 am 
Newbie

Joined: Fri Nov 10, 2006 6:37 pm
Posts: 9
From my experience, 'ConcurrentModificationException' is not a hibernate issue, but a Java issue. You might be iterating your map somewhere else, while your "onDelete" method is making changes to the same map.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Nov 17, 2006 11:01 am 
Newbie

Joined: Fri Nov 17, 2006 9:40 am
Posts: 11
Location: Switzerland
@ajay662: thanks for your reply, but I believe the stack trace clearly shows this is a hibernate issue (though not in the Hibernate code itself, but our buggy setup).

@all: Is it possible for Hibernate to have a cascade "stream" A kick off another cascade "stream" B which will eventually modify the collection the "stream" A was currently working on? Say ..

order.delete() -> delete order items -> delete invoiced order items -> delete invoices

but also
order.delete() -> delete invoices

Is this possible or does Hibernate code prevent this?

Thanks
Simon


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 23, 2006 3:43 am 
Newbie

Joined: Fri Nov 17, 2006 9:40 am
Posts: 11
Location: Switzerland
Hibernate does not like additional data on many-to-many relationships. If a map contains just references to the two entities, define the relationship as many-to-may will do the trick. But if the map contains additional data, or combines more than two entities, a separate Map class is required.

In this case, we eliminated most cascade rules from and to that map class, and handle all the add() and remove() in a separate service class. That solved the problem.

Cheers
Simon


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.