Hello, I have a complex auto wiring scenario that seems to be only happening in production.

Affected versions:

  • Spring Core 5.3.27
  • Spring Batch 4.3.8
  • Spring ORM 5.3.27
  • Hibernate 5.4.33
  • Note I am not using Spring Boot at all.

In particular, I have a transactionManager bean that is a HibernateTransactionManager and another transactionManagerTarget bean that is of type DataSourceTransactionManager. The doGetTransaction() method is failing at runtime because of a ClassCastException.

While I am not able to reproduce the issue to send as a sample project (I tried), I wanted to offer this small rewrite. It is 100% backwards compatible and simply routes into the else block instead of throwing a ClassCastException at runtime. I would greatly appreciate if it could be considered in an upcoming release!

  • instanceof check automatically handles the != null case
  • rewriting cast to avoid potential ClassCastException scenarios
  • specifically avoiding runtime java.lang.ClassCastException: org.springframework.orm.jpa.EntityManagerHolder cannot be cast to org.springframework.orm.hibernate5.SessionHolder

Lastly, here is the original stack trace. Thank you!

Caused by: java.lang.ClassCastException: org.springframework.orm.jpa.EntityManagerHolder cannot be cast to org.springframework.orm.hibernate5.SessionHolder
    at org.springframework.orm.hibernate5.HibernateTransactionManager.doGetTransaction(HibernateTransactionManager.java:425)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:347)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:241)
    at com.sun.proxy.$Proxy74.updateExecutionContext(Unknown Source)~[?:?]
    at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:452)
    ... 14 more

Comment From: snicoll

Unfortunately, that would be hiding the actual issue rather than fixing it. Without a test that demonstrates why this is legit to be lenient like that, this PR isn't actionable. And we wouldn't make such a change in 5.3.x at this point either.

Chatting with @jhoeller, it looks like you have a case with a nested transaction and incompatible resource management, which must be fixed in your application. Perhaps the outer transaction is using JPATransactionManager that ends up delegating to an inner transaction using HibernateTransactionManager, pointing to the same Hibernate SessionFactory instance?

Comment From: NaanProphet

Hi! Thanks for your reply. No problem, it sense that you need a true RCA to consider the PR, so I will keep trying to recreate a test to present.

I appreciate the tip about the nested transaction scenario - I will look into that more closely!