如何处理UnexpectedRollbackException?

时间:2015-08-24 12:39:01

标签: java spring

我在春季申请中获得UnexpectedRollbackException。这是我的Repository类

@Repository
public class MyDao {

    @PersistenceContext
    EntityManager em;

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

这是我的服务类

@Service
public class MyService{
    @Autowired
    Mydao myDao;

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

我在这里调用服务方法。

@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有一些限制,所以我期待ConstraintViolationException。我需要做的就是,如果抛出ConstraintViolationException,则默默地失败。由于服务方法中没有其他数据库操作,我想我不需要在这里回滚。但是当违反约束时,我得到以下异常。我在这里做错了什么?

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

3 个答案:

答案 0 :(得分:1)

&#34;我需要做的就是,如果抛出ConstraintViolationException,则静默失败。由于服务方法中没有其他数据库操作,我想我不需要在这里回滚。&#34;

这是Spring在你指示之前不知道的。您遇到的是:

  • 您开始交易
  • 执行数据库操作
  • 你的数据库操作失败(你没关系)
  • 由于您预期的异常(ConstraintViolation)实际上并未预期(您没有处理它),它会到达事务分割点
  • 此时Spring假定需要回滚整个事务。

因此出现UnexpectedRollbackException。 要防止出现这种情况,您唯一需要做的就是捕获(并且如果您真的想忽略此问题则吞下)事务中的原始ConstraintViolationException 。例如,在saveMyData()内部就可以了。

答案 1 :(得分:0)

我设计流程的方式确实非常糟糕。

我重新设计了流程。在尝试在数据库中插入对象之前,我正在根据约束进行选择查询。如果查询找到任何对象,我就会抛出自定义异常DataAlreadyExistsException。否则,我正在插入。

这解决了我的问题。感谢所有帮助我找到解决方案的人。

答案 2 :(得分:0)

当您尝试将数据保存到数据库并且存在可能重复的记录(根据您的列设置)时,您应该更改代码,如下所示:

1创建非事务性方法以检查您的数据是否存在

2(如果存在)发送有关它的信息

3,否则-保存数据而不会出错

示例:

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Repository;
import pl.ccy.ccy.api.service.domain.RegisterData;
import pl.ccy.ccy.api.service.domain.RegisterResult;
import pl.ccy.ccy.api.service.domain.User;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;


@Repository
@Profile("prod")
public class UserRepository {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public RegisterResult register(RegisterData registerData) {
        User user = new User();
        user.setUserEmail(registerData.getEmail());
        user.setUserPassword(registerData.getPassword());
        user.setUserPcKey(registerData.getPcKey());
        user.setUserStatus(123);

        if (isUserExist(user.getUserEmail())) {
            return new RegisterResult(false, "user exist");
        }

        em.persist(user);
        em.flush(); **<- IMPORTANT TO GET ID BACK**

        return new RegisterResult(true, user.getId(), user.getUserEmail(), user.getUserStatus());
    }

    private boolean isUserExist(String email) {
        try {
            User user = em.createQuery("from User where userEmail='" + email + "'", User.class).getSingleResult();
            if (user == null) return false;
        } catch (NoResultException exception) {
            return false;
        }
        return true;
    }