During my testing of an application i observed a strange behavior with cached collections.
It seems to me, that the collections changed during a transaction are not flushed / removed at transaction rollback.
As far as i can see, changed instances are correctly flushed.
This behavior has been observed using EhCache and OSCache.
To nail down the problem, i've implemented an Cache-Listener to get some information on the usage patterns of this collections cache element. The output from this listener is part of this article below.
It is quite surprising for me that within one test case, the order of two collection entries changed from [1, 2] to [2, 1]. In this case during rollback the collection entry has been flushed.
Another test-case on this topic shows, that a new item has been added to this collection [1, 2, 88]. This change to the collection is not flushed at transaction rollback. Therefore a later test-case (correctly) fails when retrieving this instance with id 88.
Hibernate version:
3.1.2
Mapping documents:
Code:
<class name="Actor"
table="actor"
lazy="true">
<cache usage="nonstrict-read-write" />
<id name="actorId"
column="actor_id"
type="long"
unsaved-value="any">
<generator class="native" />
</id>
<set name="roles"
table="actor_role"
lazy="true"
cascade="all"
inverse="true">
<cache usage="nonstrict-read-write" />
<key column="actor_id" />
<one-to-many class="ActorRole" />
</set>
</class>
Code between sessionFactory.openSession() and session.close():Full stack trace of any exception that occurs:Code:
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.bearingpoint.bepogo.user.domain.ActorRole#88]
at org.hibernate.ObjectNotFoundException.throwIfNull(ObjectNotFoundException.java:27)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:128)
at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:177)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:87)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:891)
at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:859)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:266)
at org.hibernate.type.ManyToOneType.assemble(ManyToOneType.java:177)
at org.hibernate.collection.PersistentSet.initializeFromCache(PersistentSet.java:101)
at org.hibernate.cache.entry.CollectionCacheEntry.assemble(CollectionCacheEntry.java:35)
at org.hibernate.event.def.DefaultInitializeCollectionEventListener.initializeCollectionFromCache(DefaultInitializeCollectionEventListener.java:130)
at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:48)
at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1695)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:344)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:138)
at com.bearingpoint.core.iterator.ClassRestrictedIterator.<init>(ClassRestrictedIterator.java:91)
at com.bearingpoint.bepogo.user.domain.SecurityRole.checkRole(SecurityRole.java:127)
at com.bearingpoint.bepogo.user.service.ActorService.inactivateActor(ActorService.java:459)
at com.bearingpoint.bepogo.user.service.ActorService$$FastClassByCGLIB$$3ea98917.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:698)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:148)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:643)
at com.bearingpoint.bepogo.user.service.ActorService$$EnhancerByCGLIB$$cedd9b76.inactivateActor(<generated>)
at com.bearingpoint.bepogo.user.test.ActorServiceTest.testInactivateActorAdmin(ActorServiceTest.java:528)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at junit.framework.TestCase.runTest(TestCase.java:154)
at com.bearingpoint.bepogo.test.SpringTestCase.runTestImpl(SpringTestCase.java:165)
at com.bearingpoint.bepogo.test.SpringTestCase$1.doInTransaction(SpringTestCase.java:186)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:114)
at com.bearingpoint.bepogo.test.SpringTestCase.runTest(SpringTestCase.java:175)
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 junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
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)
Name and version of the database you are using:MySQL 5.0.x
Debug level Hibernate log excerpt:Code:
roles - MISS:com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles
EntryAdded: com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles // CollectionCacheEntry[2,1]
******** ROLLBACK
roles - HIT:com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles // CollectionCacheEntry[2,1]
EntryFlushed: com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles
******** ROLLBACK
EntryFlushed: com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles
EntryFlushed: com.bearingpoint.bepogo.user.domain.Actor.roles#59.com.bearingpoint.bepogo.user.domain.Actor.roles
******** ROLLBACK
roles - STALE_HIT:com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles // CollectionCacheEntry[2,1]
EntryUpdated: com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles // CollectionCacheEntry[1,2]
EntryFlushed: com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles
******** ROLLBACK
EntryFlushed: com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles
roles - STALE_HIT:com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles // CollectionCacheEntry[1,2]
EntryUpdated: com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles // CollectionCacheEntry[2,1,88]
******** ROLLBACK
roles - HIT:com.bearingpoint.bepogo.user.domain.Actor.roles#2.com.bearingpoint.bepogo.user.domain.Actor.roles // CollectionCacheEntry[2,1,88]
******** EXCEPTION