Transaction rolled back because it has been marked as rollback-only,
中文翻译为:事务已回滚,因为它被标记成了只回滚。

这个异常,相信写代码多年的大家,都遇到过,什么原因呢?今天我们专门分析一下,以为前车之鉴。

报错信息详情

关键报错信息:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

详细报错信息如下:

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:485)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
	at java.util.ArrayList.forEach(ArrayList.java:1249)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
	at sun.reflect.GeneratedMethodAccessor3052.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:620)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:609)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
	at com.dangdang.ddframe.job.executor.type.SimpleJobExecutor.process(SimpleJobExecutor.java:41)
	at com.dangdang.ddframe.job.executor.AbstractElasticJobExecutor.process(AbstractElasticJobExecutor.java:207)
	at com.dangdang.ddframe.job.executor.AbstractElasticJobExecutor.access$000(AbstractElasticJobExecutor.java:47)
	at com.dangdang.ddframe.job.executor.AbstractElasticJobExecutor$1.run(AbstractElasticJobExecutor.java:186)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:108)
	at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:41)
	at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:77)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

通常我们的代码是这样的:

@Transactional
private void springTransactionTest(){
    update();

方案一:捕获抛出异常的代码块,并且不手动throws RuntimeException

@Transactional
private void springTransactionTest(){
    try {
        update();
    } catch (Exception e) {
        logger.info("update error:", e);

方案二:手动设置当前事务为rollbackOnly

@Transactional
private void springTransactionTest(){
	TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    update();

如果是包了2层try catch,则是下面这样的

try {
    update();
} catch (Exception e) {
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    logger.info("update error:", e);

或者这样的

try {
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    update();
} catch (Exception e) {
    logger.info("update error:", e);

方案三:@Transactional(readOnly = true, rollbackFor = Exception.class)

这种方案仅仅适用于使用了@Transactional注解的情况。

@Transactional(readOnly = true, rollbackFor = Exception.class)
private void springTransactionTest(){
    update();

关于异常回滚,Spring官方文档是这样描述的:
spring声明式事务管理默认对非检查异常和运行时异常进行事务回滚,而对检查异常则不进行回滚操作。

非检查异常,就是没有try catch的异常。
检查异常,就是try catch的异常(catch中不能再抛出异常)。

来,看源码,有理有据!

AbstractPlatformTransactionManager.commit() 源码

* This implementation of commit handles participating in existing * transactions and programmatic rollback requests. * Delegates to {@code isRollbackOnly}, {@code doCommit} * and {@code rollback}. * @see org.springframework.transaction.TransactionStatus#isRollbackOnly() * @see #doCommit * @see #rollback @Override public final void commit(TransactionStatus status) throws TransactionException { if (status.isCompleted()) { throw new IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; // 方案二,手动设置rollbackOnly,其实就是满足了这个场景。 defStatus.isLocalRollbackOnly() = true // 满足条件后,直接从这里执行回滚的逻辑,并返回了。 if (defStatus.isLocalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Transactional code has requested rollback"); processRollback(defStatus); return; // 方案一,捕获异常,其实就是满足了这个场景 // 我们一旦捕获了异常,并且没有向外抛出异常,那么其实jvm就感知不到发生了异常。 if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); processRollback(defStatus); // Throw UnexpectedRollbackException only at outermost transaction boundary // or if explicitly asked to. if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { throw new UnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); return; processCommit(defStatus); Transaction rolled back because it has been marked as rollback-only,中文翻译为:事务已回滚,因为它被标记成了只回滚。这个异常,相信写代码多年的大家,都遇到过,什么原因呢?今天我们专门分析一下,以为前车之鉴。报错信息详情关键报错信息:org.springframework.transaction.Un... org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only at org.springframework.transaction.support.AbstractPlatformTransactionManager.com.
java.lang.reflect.InvocationTargetException InvocationTargetException是一种包装由调用方法或构造方法所抛出异常的受查异常。这个异常并不是Eclipse插件开发特有的,而是标准JDK的,它定义在 java.lang.reflect包下。在进行Java开发的时候很少会接触到这个异常,不过在进行Eclipse插件开发则不同,很多API都声明抛出此类异常,因此必须对此异常进行处理。 A→B→C 产生原因:举例说明 下面ABC代表三个类..
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransact
1.简述JavaError类与Exception类的区别 Error异常Exception异常都继承于throwable异常类。 Error不是程序需要捕获和进行处理的,例如OutOfMemoryError(当java虚拟机在为对象分配内存空间时,剩余的空间不够,同时也没有可以释放的内容时,将会发生这样的错误)不由程序员进行捕获和处理,当Error发生时,程序将会停止。 RuntimeException异常主要包括四种异常:空指针异常,数组下标越界异常、类型转换异常、算术异常。由java虚拟机自动抛出..
PROPAGATION_REQUIRED 是 Spring 事务传播行为的一种。它的含义是:如果当前存在事务,则加入该事务,如果当前不存在事务,则开启一个新的事务。 具体来说,如果一个方法使用 PROPAGATION_REQUIRED 传播行为进行事务控制,那么当该方法被另一个使用事务的方法调用时,它会加入到该事务执行;当该方法被非事务方法调用时,它会开启一个新的事务来执行。 在使用 PROPAGATION_REQUIRED 传播行为时,如果当前不存在事务,则会开启一个新的事务;如果当前存在事务,则会加入到该事务执行。如果在执行过程发生异常Spring 事务管理器会回滚事务,并抛出事务异常。 PROPAGATION_REQUIRED 传播行为通常用于增删改等更新操作,因为这些操作需要对数据库进行修改,需要使用事务来保证数据的一致性和完整性。通过使用 PROPAGATION_REQUIRED 传播行为,可以保证多个更新操作在同一个事务执行,从而保证数据的一致性和完整性。