Spring always creates the new Transaction for REQUIRES_NEW, if an already existing transaction is present or not. but what is the situation where spring does not create new and instead participates in existing transactions?
Below is the situation where new transaction is not getting created and instead participates in existing.
All the methods, mainMethod, methodA(), methodB() and logExceptionMethod() are in different beans, Ideally, When methodB() fails, exception will get caught in catch block, and logExceptionMethod() will audit the log by creating new transaction.
But here new transaction is not started for the logExceptionMethod() method even though, this method is annotated with requires_new and is Public and in different bean, instead it participates in existing transaction which result in rollback
mainMethod - Propagation.REQUIRES methodA - Propagation.REQUIRES_NEW methodB - not annotated logExceptionMethod - Propagation.REQUIRES_NEW
Flow
@Transactional
mainMethod() {
try{
methodA();
} catch(Exception e) {
throw e;
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
methodA(){
try{
methodB()
}
catch(Exception e) {
throw e;
}
}
methodB(){
try {
// api call gives exception
}
catch(Exception e) {
logExceptionMethod();
throw e;
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
logExceptionMethod() {
// create log entity
// store entity in db
}
Logs
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Creating new transaction with name [mainMethod]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@53f498b2]
TRACE o.s.t.i.TransactionInterceptor - 1F2EA187-C884-461C-8183-328A4B7621D0 - Getting transaction for [mainMethod]
TRACE o.s.d.r.c.s.TransactionalRepositoryProxyPostProcessor$RepositoryAnnotationTransactionAttributeSource - 1F2EA187-C884-461C-8183-328A4B7621D0 - Adding transactional method 2023-08-11 03:04:13 [qtp1482177069-1382]
TRACE o.s.b.f.s.DefaultListableBeanFactory - 1F2EA187-C884-461C-8183-328A4B7621D0 - Returning cached instance of singleton bean 'transactionManager'
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Found thread-bound EntityManager [SessionImpl(1543398267<open>)] for JPA transaction
INFO - 1F2EA187-C884-461C-8183-328A4B7621D0 - Current transaction : mainMethod
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Found thread-bound EntityManager [SessionImpl(1543398267<open>)] for JPA transaction
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Suspending current transaction, creating new transaction with name [methodA]
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Opened new EntityManager [SessionImpl(1186799888<open>)] for JPA transaction
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@54c9937c]
TRACE o.s.t.i.TransactionInterceptor - 1F2EA187-C884-461C-8183-328A4B7621D0 - Getting transaction for [methodA]
INFO- 1F2EA187-C884-461C-8183-328A4B7621D0 - Current transaction : methodA
ERROR - 1F2EA187-C884-461C-8183-328A4B7621D0 - Exception Occured
TRACE o.s.d.r.c.s.TransactionalRepositoryProxyPostProcessor$RepositoryAnnotationTransactionAttributeSource - 1F2EA187-C884-461C-8183-328A4B7621D0 - Adding transactional method 'org.springframework.data.jpa.repository.support.SimpleJpaRepository.save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
TRACE o.s.b.f.s.DefaultListableBeanFactory - 1F2EA187-C884-461C-8183-328A4B7621D0 - Returning cached instance of singleton bean
'transactionManager'
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Found thread-bound EntityManager [SessionImpl(1186799888<open>)] for JPA transaction
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Participating in existing transaction
INFO - 1F2EA187-C884-461C-8183-328A4B7621D0 - Added entry in log
TRACE o.s.t.i.TransactionInterceptor - 1F2EA187-C884-461C-8183-328A4B7621D0 - Completing transaction for [methodA] after exception:
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Initiating transaction rollback
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Rolling back JPA transaction on EntityManager [SessionImpl(1186799888<open>)]
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Closing JPA EntityManager [SessionImpl(1186799888<open>)] after transaction
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Resuming suspended transaction after completion of inner transaction
TRACE o.s.t.i.TransactionInterceptor - 1F2EA187-C884-461C-8183-328A4B7621D0 - Completing transaction for [mainMethod] after exception:
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Initiating transaction rollback
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Rolling back JPA transaction on EntityManager [SessionImpl(1543398267<open>)]
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Not closing pre-bound JPA EntityManager after transaction
Now I can use specific noRollbackFor property to prevent rollback the transaction, but still, the main question remains is
- why does Spring have not created new transaction?
- what is the situation where this scenario can happen?