Hibernate version: 3.2.2.ga
JBoss Cache version: 1.4.1.sp2
Hibernate config XML:
Code:
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:mem:temp</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<!--property name="current_session_context_class">thread</property-->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="cache.provider_class">org.hibernate.cache.OptimisticTreeCacheProvider</property>
<property name="transaction.manager_lookup_class">com.medq.test.DummyTransactionManagerLookup</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<mapping class="com.medq.test.A"/>
<mapping class="com.medq.test.B"/>
</session-factory>
</hibernate-configuration>
Annotated entities:Code:
@Entity
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL)
public class A {
@Id
Integer id;
String s;
@OneToMany(fetch=FetchType.EAGER, mappedBy="a", cascade=CascadeType.ALL)
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL)
List<B> bs = new LinkedList<B>();
}
Code:
@Entity
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL)
public class B {
@Id
int id;
@ManyToOne
A a;
}
Code between sessionFactory.openSession() and session.close():Code:
// Use dummy transaction stuff provided by JBoss Cache for this test code
UserTransaction tx= new DummyUserTransaction(DummyTransactionManager.getInstance());
tx.begin();
A a = new A();
a.id = 1;
session.persist(a);
session.flush();
session.clear();
tx.commit();
tx.begin();
session.get(A.class, 1);
session.createSQLQuery("update A set s = 'blah'").executeUpdate(); // kaboom!!!
tx.commit();
Full stack trace of any exception that occurs:Code:
org.hibernate.cache.CacheException: org.jboss.cache.CacheException: Unable to find parent node with Fqn /com/medq/test/A
at org.hibernate.cache.OptimisticTreeCache.clear(OptimisticTreeCache.java:169)
at org.hibernate.cache.TransactionalCache.clear(TransactionalCache.java:124)
at org.hibernate.impl.SessionFactoryImpl.evictCollection(SessionFactoryImpl.java:874)
at org.hibernate.action.BulkOperationCleanupAction.evictCollectionRegions(BulkOperationCleanupAction.java:142)
at org.hibernate.action.BulkOperationCleanupAction.init(BulkOperationCleanupAction.java:103)
at org.hibernate.engine.query.NativeSQLQueryPlan.coordinateSharedCacheCleanup(NativeSQLQueryPlan.java:134)
at org.hibernate.engine.query.NativeSQLQueryPlan.performExecuteUpdate(NativeSQLQueryPlan.java:144)
at org.hibernate.impl.SessionImpl.executeNativeUpdate(SessionImpl.java:1163)
at org.hibernate.impl.SQLQueryImpl.executeUpdate(SQLQueryImpl.java:334)
at com.medq.test.TestHibernate.main(TestHibernate.java:39)
Caused by: org.jboss.cache.CacheException: Unable to find parent node with Fqn /com/medq/test/A
at org.jboss.cache.interceptors.OptimisticNodeInterceptor.removeNode(OptimisticNodeInterceptor.java:218)
at org.jboss.cache.interceptors.OptimisticNodeInterceptor.invoke(OptimisticNodeInterceptor.java:110)
at org.jboss.cache.interceptors.Interceptor.invoke(Interceptor.java:68)
at org.jboss.cache.interceptors.EvictionInterceptor.invoke(EvictionInterceptor.java:88)
at org.jboss.cache.interceptors.Interceptor.invoke(Interceptor.java:68)
at org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor.invoke(OptimisticCreateIfNotExistsInterceptor.java:69)
at org.jboss.cache.interceptors.Interceptor.invoke(Interceptor.java:68)
at org.jboss.cache.interceptors.OptimisticValidatorInterceptor.invoke(OptimisticValidatorInterceptor.java:84)
at org.jboss.cache.interceptors.Interceptor.invoke(Interceptor.java:68)
at org.jboss.cache.interceptors.OptimisticLockingInterceptor.invoke(OptimisticLockingInterceptor.java:126)
at org.jboss.cache.interceptors.Interceptor.invoke(Interceptor.java:68)
at org.jboss.cache.interceptors.TxInterceptor.handleNonTxMethod(TxInterceptor.java:365)
at org.jboss.cache.interceptors.TxInterceptor.invoke(TxInterceptor.java:160)
at org.jboss.cache.interceptors.Interceptor.invoke(Interceptor.java:68)
at org.jboss.cache.interceptors.CacheMgmtInterceptor.invoke(CacheMgmtInterceptor.java:183)
at org.jboss.cache.TreeCache.invokeMethod(TreeCache.java:5776)
at org.jboss.cache.TreeCache.remove(TreeCache.java:3855)
at org.jboss.cache.TreeCache.remove(TreeCache.java:3438)
at org.hibernate.cache.OptimisticTreeCache.clear(OptimisticTreeCache.java:166)
... 9 more
Name and version of the database you are using: HSQLDB 1.8.0
The generated SQL:Code:
Hibernate: insert into A (s, id) values (?, ?)
Hibernate: select bs0_.a_id as a2_1_, bs0_.id as id1_, bs0_.id as id1_0_, bs0_.a_id as a2_1_0_ from B bs0_ where bs0_.a_id=?
We saw this problem in our JBoss/EJB3 application. I reproduced it in a test J2SE application, the code from which is posted above.
The problem occurs when Hibernate attempts to evict the A.bs collection. Because the A entity has already been evicted by BulkOperationCleanupAction, JBoss Cache can't find a WorkspaceNode for the parent of A.bs.
This is what JBoss Cache sees Hibernate doing (in pseudo-code):
Code:
put com/medq/test/A/bs/com.medq.test.A.bs#1 key value
remove /com/medq/test/B
remove /com/medq/test/A
remove /com/medq/test/A/bs // kaboom!!!
Obviously removing "/com/medq/test/A" also removes "/com/medq/test/A/bs", so the last line is superfluous (as well as causing an exception to be thrown).
Is there a reason why
org.hibernate.cache.OptimisticTreeCache.clear doesn't call
option.setFailSilently(true)? The exception above would be swallowed by JBoss Cache if it did.