0

I have a Spring Boot application where I have a thin layer between the Controller and the Service which's only purpose it to try-catch and if an exception is thrown, to persist the failed entity with a JpaRepository, for subsequent inspection. I designed my "interceptor" like:

    @Transactional
    public void upload(byte[] bytes) {
       try {
          service.upload(bytes);
       } catch (Exception e) {
           failRepo.save(new Failure(bytes, e)); // code trimmed for brevity 
           throw e;
       }
   }

And my service method looks like:

@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void upload(byte[] bytes);

What I expect is that, in case of an exception thrown in the service, the inner transaction will be rollbacked, the exception will pop out and the outer transaction will persist my data, but the hibernate logs show that for some reason the outer transaction also rollbacks and the failure data is not persisted. Do I need another propagation level on the outer layer also?

Edit: the relevant logs

o.s.o.h.HibernateTransactionManager      : Creating new transaction with name [com.company.interceptor.upload]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.o.h.HibernateTransactionManager      : Opened new Session [SessionImpl(480607911<open>)] for Hibernate transaction
o.h.e.t.internal.TransactionImpl         : On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
o.h.e.t.internal.TransactionImpl         : begin
o.s.o.h.HibernateTransactionManager      : Exposing Hibernate transaction as JDBC [org.springframework.orm.hibernate5.HibernateTransactionManager$$Lambda$1317/0x0000000800d0c440@52318830]
o.s.o.h.HibernateTransactionManager      : Found thread-bound Session [SessionImpl(480607911<open>)] for Hibernate transaction
o.s.o.h.HibernateTransactionManager      : Suspending current transaction, creating new transaction with name [com.company.service.upload]
o.s.o.h.HibernateTransactionManager      : Opened new Session [SessionImpl(1041560268<open>)] for Hibernate transaction
o.h.e.t.internal.TransactionImpl         : On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
o.h.e.t.internal.TransactionImpl         : begin
o.s.o.h.HibernateTransactionManager      : Exposing Hibernate transaction as JDBC [org.springframework.orm.hibernate5.HibernateTransactionManager$$Lambda$1317/0x0000000800d0c440@778c9da3]

------ other irrelevant for us logs

o.s.o.h.HibernateTransactionManager      : Initiating transaction rollback
o.s.o.h.HibernateTransactionManager      : Rolling back Hibernate transaction on Session [SessionImpl(1041560268<open>)]
o.h.e.t.internal.TransactionImpl         : rolling back
o.s.o.h.HibernateTransactionManager      : Closing Hibernate Session [SessionImpl(1041560268<open>)] after transaction
o.s.o.h.HibernateTransactionManager      : Resuming suspended transaction after completion of inner transaction
stomAnnotationTransactionAttributeSource : Adding transactional method 'save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.o.h.HibernateTransactionManager      : Found thread-bound Session [SessionImpl(480607911<open>)] for Hibernate transaction
o.s.o.h.HibernateTransactionManager      : Participating in existing transaction
org.hibernate.engine.spi.ActionQueue     : Executing identity-insert immediately
org.hibernate.SQL                        : insert into failure_table (content_type, created_time, exception_message, bytes, user_agent) values (?, ?, ?, ?, ?)
o.h.id.IdentifierGeneratorHelper         : Natively generated identity: 15
o.h.r.j.i.ResourceRegistryStandardImpl   : HHH000387: ResultSet's statement was not registered
o.s.o.h.HibernateTransactionManager      : Initiating transaction rollback
o.s.o.h.HibernateTransactionManager      : Rolling back Hibernate transaction on Session [SessionImpl(480607911<open>)]
o.h.e.t.internal.TransactionImpl         : rolling back
o.s.o.h.HibernateTransactionManager      : Closing Hibernate Session [SessionImpl(480607911<open>)] after transaction
Daniel Pop
  • 456
  • 1
  • 6
  • 23
  • Please provide the logs. Also, are you able to check the code flow in debug mode and see whether the exception is trapped? – aksappy Mar 22 '21 at 20:06
  • @aksappy yep, exception is successfully caught in the catch clause, the Failure object is created just ok, the exception is rethrown as it should. The only problem is that the failure is not persisted. Updated the question to contain the relevant logs. – Daniel Pop Mar 23 '21 at 06:16
  • Please provide your source code. I can't see where you insert something in the outer transcation – Simon Martinelli Mar 23 '21 at 06:50
  • FailRepo is a JpaRepository on which I call the default save() method. Edited the question. – Daniel Pop Mar 23 '21 at 06:55

1 Answers1

0

Problem solution helped found by: https://stackoverflow.com/a/7125918/3214777.

The problem was that Spring rollbacks by default on runtime exception so I needed an

@Transactional(noRollbackFor = Exception.class)

on my interceptor to get it going as I would've expected.

Daniel Pop
  • 456
  • 1
  • 6
  • 23