Nayan
Nayan

Reputation: 1605

How to handle UnexpectedRollbackException?

I'm getting UnexpectedRollbackException in my spring application. Here is my Repository class

@Repository
public class MyDao {

    @PersistenceContext
    EntityManager em;

    public MyData save(MyData myData){
        return em.merge(myData)
    }
}

Here is my Service class

@Service
public class MyService{
    @Autowired
    Mydao myDao;

    @Transactional
    public void saveMyData(MyData myData){
        myDao.save(myData);
    }
}

Here I'm calling the service method.

@Component
public class ScheduledService{

    @Autowired
    MyService myService;

    @Scheduled(fixedDelay=60000)
    public void myDataScheduler(){
        // ... create mydata object
        for(int i = 0; i < 5; i++)
            myService.saveMyData(myData);
    }
}

MyData has some constraints so I'm expecting ConstraintViolationException. All I need to do is, if ConstraintViolationException is thrown, fail silently. As there is no other DB operation inside the service method, I guess I don't need rollback here. But I'm getting the following exception when a constraint violates. What I'm doing wrong here?

2015-08-24 18:01:28,677 SEVERE [org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler] (pool-5-thread-1) Unexpected error occurred in scheduled task.: org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
    at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1024) [spring-tx-4.0.7.RELEASE.jar:4.0.7.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757) [spring-tx-4.0.7.RELEASE.jar:4.0.7.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726) [spring-tx-4.0.7.RELEASE.jar:4.0.7.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478) [spring-tx-4.0.7.RELEASE.jar:4.0.7.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272) [spring-tx-4.0.7.RELEASE.jar:4.0.7.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) [spring-tx-4.0.7.RELEASE.jar:4.0.7.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653) [spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at com.myapp.service.saveMyData() [spring-core-4.1.6.RELEASE.jar:]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.7.0_75]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) [rt.jar:1.7.0_75]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.7.0_75]
    at java.lang.reflect.Method.invoke(Method.java:606) [rt.jar:1.7.0_75]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) [spring-context-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [spring-context-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) [rt.jar:1.7.0_75]
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304) [rt.jar:1.7.0_75]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178) [rt.jar:1.7.0_75]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [rt.jar:1.7.0_75]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_75]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_75]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_75]
Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
    at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1178)
    at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:126)
    at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
    at org.jboss.tm.usertx.client.ServerVMClientUserTransaction.commit(ServerVMClientUserTransaction.java:173)
    at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1021) [spring-tx-4.0.7.RELEASE.jar:4.0.7.RELEASE]
    ... 24 more
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl$CallbackExceptionMapperImpl.mapManagedFlushFailure(AbstractEntityManagerImpl.java:1882) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.engine.transaction.synchronization.internal.SynchronizationCallbackCoordinatorNonTrackingImpl.beforeCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:119) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:50) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:76)
    at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:358)
    at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:91)
    at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:162)
    at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1166)
    ... 28 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
    at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:72) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:211) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:62) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3124) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3581) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:104) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:349) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.engine.transaction.synchronization.internal.SynchronizationCallbackCoordinatorNonTrackingImpl.beforeCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:114) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    ... 34 more
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (IM.UK_K4P6Q9JG3ANIQKAWWAI65TWX5) violated

    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:450)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:399)
    at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1059)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:522)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:257)
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:587)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:225)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:53)
    at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:943)
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1150)
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:4798)
    at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:4875)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1361)
    at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeUpdate(WrappedPreparedStatement.java:493)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    ... 45 more

Upvotes: 4

Views: 17714

Answers (2)

Nayan
Nayan

Reputation: 1605

The way I've designed the flow was a very bad one indeed.

I've redesigned the flow. Before trying to insert the object in the DB, I'm doing a select query based on the constraints. If the query finds any objects, I'm throwing a custom exception DataAlreadyExistsException. Otherwise I'm doing the insertion.

This solved my problem. Thanks to all who helped me in figuring out the solution.

Upvotes: 2

Gergely Bacso
Gergely Bacso

Reputation: 14661

"All I need to do is, if ConstraintViolationException is thrown, fail silently. As there is no other DB operation inside the service method, I guess I don't need rollback here."

This is what Spring does not know until you instruct it. What you are experiencing is this:

  • you start a transaction
  • you execute a DB operation
  • your DB operation fails (and you are ok with this)
  • As your expected exception (ConstraintViolation) was not actually expected (you did not handle it) it reaches the transaction cutpoint
  • at this point Spring assumes that the whole transaction needs to be rolled back.

Hence the UnexpectedRollbackException. To prevent this, the only thing you need to do is catch (and swallow, if you really want to ignore this problem) the original ConstraintViolationException WITHIN the transaction. Inside the saveMyData() for example would be ok.

Upvotes: 1

Related Questions