之前经常报"Transaction rolled back because it has been marked as rollback-only"这个异常

字面意思是"事务回滚了,因为它被标记了必须回滚",最开始完全不懂事务的嵌套,每次出现这个错误都想知道为什么,但是总是不能重现,后面反复折腾终于弄明白了怎么回事。

之前不能重现的一个重要原因是:同一个类,内部方法调用不走代理,spring基于注解的事务是基于代理的,不走代理,被调用的方法就不受事务管理代码的控制,自然无法重现问题.

测试代码:

TestController

 
  1. @Autowired

  2. TestRollbackService testRollbackService;

  3. @RequestMapping("/test1")

  4. public void test1(){

  5. try{

  6. testRollbackService.test1();

  7. }catch(Exception e){

  8. e.printStackTrace();

  9. }

  10. }

TestRollbackServiceImpl

 
  1. @Autowired

  2. StudentMapper studentMapper;

  3. @Autowired

  4. TestTransactionService testTransactionService;

  5. @Transactional(rollbackFor = Exception.class)

  6. public void test1(){

  7. Student studentSelect = new Student();

  8. studentSelect.setId(new Long(1));

  9. Student student = studentMapper.selectByPrimaryKey(studentSelect);

  10. try{

  11. testTransactionService.test2();

  12. }catch(Exception e){

  13. e.printStackTrace();

  14. }

  15. }

TestTransactionServiceImpl

 
  1. @Autowired

  2. StudentMapper studentMapper;

  3. @Transactional(rollbackFor = Exception.class)

  4. public void test2(){

  5. Student studentForInsert = new Student();

  6. studentForInsert.setId(new Long(19));

  7. studentForInsert.setName("测试11");

  8. studentForInsert.setScore(new BigDecimal(69));

  9. studentMapper.updateByPrimaryKey(studentForInsert);

  10. System.out.println(1/0);

  11. }

TestRollbackService.test1(方法A)中调用了TestTransactionService.test2(方法B),上述代码可以触发回滚异常的报错

两个方法都加了事务注解,并且两个方法都会受到到事务管理的拦截器增强,并且事务传播的方式都是默认的,也就是REQUIRED,当已经存在事务的时候就加入事务,没有就创建事务。这里A和B都受事务控制,并且是处于同一个事务的。

A调用B,A中抓了B的异常,当B发生异常的时候,B的操作应该回滚,但是A吃了异常,A方法中没有产生异常,所以A的操作又应该提交,二者是相互矛盾的。

spring的事务关联拦截器在抓到B的异常后就会标记rollback-only为true,当A执行完准备提交后,发现rollback-only为true,也会回滚,并抛出异常告诉调用者。

程序时序图如下:

1.程序入口

程序入口肯定是代理类,这里走是cglib的代理

入口方法:

org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept

该方法中

retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();

继续调用

org.springframework.aop.framework.ReflectiveMethodInvocation#proceed

然后到了事务管理的拦截器

org.springframework.transaction.interceptor.TransactionInterceptor#invoke

 
  1. @Nullable

  2. public Object invoke(MethodInvocation invocation) throws Throwable {

  3. Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;

  4. Method var10001 = invocation.getMethod();

  5. invocation.getClass();

  6. return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);

  7. }

2.invokeWithinTransaction(事务管理的主方法)

 
  1. @Nullable

  2. protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable {

  3. TransactionAttributeSource tas = this.getTransactionAttributeSource();

  4. TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;

  5. PlatformTransactionManager tm = this.determineTransactionManager(txAttr);

  6. String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);

  7. Object result;

  8. if (txAttr != null && tm instanceof CallbackPreferringPlatformTransactionManager) {

  9. TransactionAspectSupport.ThrowableHolder throwableHolder = new TransactionAspectSupport.ThrowableHolder();

  10. try {

  11. result = ((CallbackPreferringPlatformTransactionManager)tm).execute(txAttr, (status) -> {

  12. TransactionAspectSupport.TransactionInfo txInfo = this.prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);

  13. Object var9;

  14. try {

  15. Object var8 = invocation.proceedWithInvocation();

  16. return var8;

  17. } catch (Throwable var13) {

  18. if (txAttr.rollbackOn(var13)) {

  19. if (var13 instanceof RuntimeException) {

  20. throw (RuntimeException)var13;

  21. }

  22. throw new TransactionAspectSupport.ThrowableHolderException(var13);

  23. }

  24. throwableHolder.throwable = var13;

  25. var9 = null;

  26. } finally {

  27. this.cleanupTransactionInfo(txInfo);

  28. }

  29. return var9;

  30. });

  31. if (throwableHolder.throwable != null) {

  32. throw throwableHolder.throwable;

  33. } else {

  34. return result;

  35. }

  36. } catch (TransactionAspectSupport.ThrowableHolderException var19) {

  37. throw var19.getCause();

  38. } catch (TransactionSystemException var20) {

  39. if (throwableHolder.throwable != null) {

  40. this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);

  41. var20.initApplicationException(throwableHolder.throwable);

  42. }

  43. throw var20;

  44. } catch (Throwable var21) {

  45. if (throwableHolder.throwable != null) {

  46. this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);

  47. }

  48. throw var21;

  49. }

  50. } else {

  51. TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

  52. result = null;

  53. try {

  54. result = invocation.proceedWithInvocation();

  55. } catch (Throwable var17) {

  56. this.completeTransactionAfterThrowing(txInfo, var17);

  57. throw var17;

  58. } finally {

  59. this.cleanupTransactionInfo(txInfo);

  60. }

  61. this.commitTransactionAfterReturning(txInfo);

  62. return result;

  63. }

  64. }

程序执行的是最后的else分支,步骤很清晰

1.获取 TransactionAttribute

2.基于TransactionAttribute获取TransactionManager

3.基于TransactionAttribute获取 joinpointIdentification(没研究什么作用)

4. 基于1,2,3创建的对象获取  TransactionAspectSupport.TransactionInfo,transactionInfo是TransactionAspectSupport的一个内部类

5.执行业务方法

6.抓到异常就回滚,并清除事务,然后向上抛异常;没有异常就清除事务,然后提交

对象之间的关联关系

3.各个对象的获取

TransactionManager的获取比较简单,程序里获取到的其实就是自己配置的bean

创建TransactionInfo的过程中要先获取TransactionStatus,TransactionStatus又需要拿到ConnectionHolder

3.1 createTransactionIfNecessary

org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary

 
  1. protected TransactionAspectSupport.TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

  2. if (txAttr != null && ((TransactionAttribute)txAttr).getName() == null) {

  3. txAttr = new DelegatingTransactionAttribute((TransactionAttribute)txAttr) {

  4. public String getName() {

  5. return joinpointIdentification;

  6. }

  7. };

  8. }

  9. TransactionStatus status = null;

  10. if (txAttr != null) {

  11. if (tm != null) {

  12. status = tm.getTransaction((TransactionDefinition)txAttr);

  13. } else if (this.logger.isDebugEnabled()) {

  14. this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured");

  15. }

  16. }

  17. return this.prepareTransactionInfo(tm, (TransactionAttribute)txAttr, joinpointIdentification, status);

  18. }

3.2 获取TransactionStatus

这里先调用   status = tm.getTransaction((TransactionDefinition)txAttr)  创建TransactionStatus

org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction

 
  1. public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {

  2. Object transaction = this.doGetTransaction();

  3. boolean debugEnabled = this.logger.isDebugEnabled();

  4. if (definition == null) {

  5. definition = new DefaultTransactionDefinition();

  6. }

  7. if (this.isExistingTransaction(transaction)) {

  8. return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);

  9. } else if (((TransactionDefinition)definition).getTimeout() < -1) {

  10. throw new InvalidTimeoutException("Invalid transaction timeout", ((TransactionDefinition)definition).getTimeout());

  11. } else if (((TransactionDefinition)definition).getPropagationBehavior() == 2) {

  12. throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");

  13. } else if (((TransactionDefinition)definition).getPropagationBehavior() != 0 && ((TransactionDefinition)definition).getPropagationBehavior() != 3 && ((TransactionDefinition)definition).getPropagationBehavior() != 6) {

  14. if (((TransactionDefinition)definition).getIsolationLevel() != -1 && this.logger.isWarnEnabled()) {

  15. this.logger.warn("Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: " + definition);

  16. }

  17. boolean newSynchronization = this.getTransactionSynchronization() == 0;

  18. return this.prepareTransactionStatus((TransactionDefinition)definition, (Object)null, true, newSynchronization, debugEnabled, (Object)null);

  19. } else {

  20. AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources = this.suspend((Object)null);

  21. if (debugEnabled) {

  22. this.logger.debug("Creating new transaction with name [" + ((TransactionDefinition)definition).getName() + "]: " + definition);

  23. }

  24. try {

  25. boolean newSynchronization = this.getTransactionSynchronization() != 2;

  26. DefaultTransactionStatus status = this.newTransactionStatus((TransactionDefinition)definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);

  27. this.doBegin(transaction, (TransactionDefinition)definition);

  28. this.prepareSynchronization(status, (TransactionDefinition)definition);

  29. return status;

  30. } catch (Error | RuntimeException var7) {

  31. this.resume((Object)null, suspendedResources);

  32. throw var7;

  33. }

  34. }

  35. }

3.3 获取transactionStatus前先获取DataSourceTransactionObject

程序最上面: Object transaction = this.doGetTransaction(); 创建DataSourceTransactionObject对象,这是DataSourceTransactionManager的内部类

org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction

 
  1. protected Object doGetTransaction() {

  2. DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject();

  3. txObject.setSavepointAllowed(this.isNestedTransactionAllowed());

  4. ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource());

  5. txObject.setConnectionHolder(conHolder, false);

  6. return txObject;

  7. }

这里还获取了ConnectionHolder对象,这里newConnectionHolder为false

获取的方法如下:

org.springframework.transaction.support.TransactionSynchronizationManager#getResource

 
  1. @Nullable

  2. public static Object getResource(Object key) {

  3. Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);

  4. Object value = doGetResource(actualKey);

  5. if (value != null && logger.isTraceEnabled()) {

  6. logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");

  7. }

  8. return value;

  9. }

看代码很有意思,好像是通过一个key获取的,类似于从一个池子里面拿东西一样,其实当A方法执行的时候并没有获取到ConnectionHolder,拿到的是null

org.springframework.transaction.support.TransactionSynchronizationManager#doGetResource

 
  1. private static Object doGetResource(Object actualKey) {

  2. Map<Object, Object> map = (Map)resources.get();

  3. if (map == null) {

  4. return null;

  5. } else {

  6. Object value = map.get(actualKey);

  7. if (value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) {

  8. map.remove(actualKey);

  9. if (map.isEmpty()) {

  10. resources.remove();

  11. }

  12. value = null;

  13. }

  14. return value;

  15. }

  16. }

resources对象其实是一个ThreadLocal,意思是同一个线程中拿到的ConnectionHolder是相同的

3.3 doBegin方法

org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin

 
  1. protected void doBegin(Object transaction, TransactionDefinition definition) {

  2. DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;

  3. Connection con = null;

  4. try {

  5. if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {

  6. Connection newCon = this.obtainDataSource().getConnection();

  7. if (this.logger.isDebugEnabled()) {

  8. this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");

  9. }

  10. txObject.setConnectionHolder(new ConnectionHolder(newCon), true);

  11. }

  12. txObject.getConnectionHolder().setSynchronizedWithTransaction(true);

  13. con = txObject.getConnectionHolder().getConnection();

  14. Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);

  15. txObject.setPreviousIsolationLevel(previousIsolationLevel);

  16. if (con.getAutoCommit()) {

  17. txObject.setMustRestoreAutoCommit(true);

  18. if (this.logger.isDebugEnabled()) {

  19. this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");

  20. }

  21. con.setAutoCommit(false);

  22. }

  23. this.prepareTransactionalConnection(con, definition);

  24. txObject.getConnectionHolder().setTransactionActive(true);

  25. int timeout = this.determineTimeout(definition);

  26. if (timeout != -1) {

  27. txObject.getConnectionHolder().setTimeoutInSeconds(timeout);

  28. }

  29. if (txObject.isNewConnectionHolder()) {

  30. TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());

  31. }

  32. } catch (Throwable var7) {

  33. if (txObject.isNewConnectionHolder()) {

  34. DataSourceUtils.releaseConnection(con, this.obtainDataSource());

  35. txObject.setConnectionHolder((ConnectionHolder)null, false);

  36. }

  37. throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", var7);

  38. }

  39. }

截取该方法的重要几行:

 
  1. Connection newCon = this.obtainDataSource().getConnection();

  2. if (this.logger.isDebugEnabled()) {

  3. this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");

  4. }

  5. txObject.setConnectionHolder(new ConnectionHolder(newCon), true);

先获取连接(java.sql.Connection),然后创建ConnectionHolder,newConnectionHolder设置为true,如果之前已经不为空了,newConnectionHolder就为false

如果newConnectionHolder 为 true,还需要将 connectionHolder放到threadLocal里面,让后面的方法可以获取到相同的ConnectionHolder,截取的代码如下:

 
  1. if (txObject.isNewConnectionHolder()) {

  2. TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());

  3. }

到这里TransactionStatus就创建好了

3.3 获取TransactionInfo

org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo

 
  1. protected TransactionAspectSupport.TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @Nullable TransactionStatus status) {

  2. TransactionAspectSupport.TransactionInfo txInfo = new TransactionAspectSupport.TransactionInfo(tm, txAttr, joinpointIdentification);

  3. if (txAttr != null) {

  4. if (this.logger.isTraceEnabled()) {

  5. this.logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");

  6. }

  7. txInfo.newTransactionStatus(status);

  8. } else if (this.logger.isTraceEnabled()) {

  9. this.logger.trace("Don't need to create transaction for [" + joinpointIdentification + "]: This method isn't transactional.");

  10. }

  11. txInfo.bindToThread();

  12. return txInfo;

  13. }

细看 txInfo.bindToThread();

org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo#bindToThread

 
  1. private void bindToThread() {

  2. this.oldTransactionInfo = (TransactionAspectSupport.TransactionInfo)TransactionAspectSupport.transactionInfoHolder.get();

  3. TransactionAspectSupport.transactionInfoHolder.set(this);

  4. }

java.lang.ThreadLocal#get 

 
  1. public T get() {

  2. Thread t = Thread.currentThread();

  3. ThreadLocalMap map = getMap(t);

  4. if (map != null) {

  5. ThreadLocalMap.Entry e = map.getEntry(this);

  6. if (e != null) {

  7. @SuppressWarnings("unchecked")

  8. T result = (T)e.value;

  9. return result;

  10. }

  11. }

  12. return setInitialValue();

  13. }

bindToThread()的的作用是获取oldTransactionInfo,还有线程有关(具体原理未知)

4.B方法抛异常后的回滚操作

org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing

 
  1. protected void completeTransactionAfterThrowing(@Nullable TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {

  2. if (txInfo != null && txInfo.getTransactionStatus() != null) {

  3. if (this.logger.isTraceEnabled()) {

  4. this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);

  5. }

  6. if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {

  7. try {

  8. txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());

  9. } catch (TransactionSystemException var6) {

  10. this.logger.error("Application exception overridden by rollback exception", ex);

  11. var6.initApplicationException(ex);

  12. throw var6;

  13. } catch (Error | RuntimeException var7) {

  14. this.logger.error("Application exception overridden by rollback exception", ex);

  15. throw var7;

  16. }

  17. } else {

  18. try {

  19. txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());

  20. } catch (TransactionSystemException var4) {

  21. this.logger.error("Application exception overridden by commit exception", ex);

  22. var4.initApplicationException(ex);

  23. throw var4;

  24. } catch (Error | RuntimeException var5) {

  25. this.logger.error("Application exception overridden by commit exception", ex);

  26. throw var5;

  27. }

  28. }

  29. }

  30. }

txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());调用transactionManager进行rollback

org.springframework.transaction.support.AbstractPlatformTransactionManager#rollback

 
  1. public final void rollback(TransactionStatus status) throws TransactionException {

  2. if (status.isCompleted()) {

  3. throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");

  4. } else {

  5. DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;

  6. this.processRollback(defStatus, false);

  7. }

  8. }

进一步调自身的processRollback

org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback

 
  1. private void processRollback(DefaultTransactionStatus status, boolean unexpected) {

  2. try {

  3. boolean unexpectedRollback = unexpected;

  4. try {

  5. this.triggerBeforeCompletion(status);

  6. if (status.hasSavepoint()) {

  7. if (status.isDebug()) {

  8. this.logger.debug("Rolling back transaction to savepoint");

  9. }

  10. status.rollbackToHeldSavepoint();

  11. } else if (status.isNewTransaction()) {

  12. if (status.isDebug()) {

  13. this.logger.debug("Initiating transaction rollback");

  14. }

  15. this.doRollback(status);

  16. } else {

  17. if (status.hasTransaction()) {

  18. if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {

  19. if (status.isDebug()) {

  20. this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback");

  21. }

  22. } else {

  23. if (status.isDebug()) {

  24. this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");

  25. }

  26. this.doSetRollbackOnly(status);

  27. }

  28. } else {

  29. this.logger.debug("Should roll back transaction but cannot - no transaction available");

  30. }

  31. if (!this.isFailEarlyOnGlobalRollbackOnly()) {

  32. unexpectedRollback = false;

  33. }

  34. }

  35. } catch (Error | RuntimeException var8) {

  36. this.triggerAfterCompletion(status, 2);

  37. throw var8;

  38. }

  39. this.triggerAfterCompletion(status, 1);

  40. if (unexpectedRollback) {

  41. throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");

  42. }

  43. } finally {

  44. this.cleanupAfterCompletion(status);

  45. }

  46. }

this.triggerBeforeCompletion(status) 这个方法好像释放了连接

B不是新事务,所以最后会执行  this.doSetRollbackOnly(status);

org.springframework.jdbc.datasource.DataSourceTransactionManager#doSetRollbackOnly

 
  1. protected void doSetRollbackOnly(DefaultTransactionStatus status) {

  2. DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();

  3. if (status.isDebug()) {

  4. this.logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "] rollback-only");

  5. }

  6. txObject.setRollbackOnly();

  7. }

org.springframework.jdbc.datasource.DataSourceTransactionManager.DataSourceTransactionObject#setRollbackOnly

 
  1. public void setRollbackOnly() {

  2. this.getConnectionHolder().setRollbackOnly();

  3. }

这里可以看到最终设置的是connectionHolder的rollbackonly属性

5.A方法尝试提交时的操作

org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning

 
  1. protected void commitTransactionAfterReturning(@Nullable TransactionAspectSupport.TransactionInfo txInfo) {

  2. if (txInfo != null && txInfo.getTransactionStatus() != null) {

  3. if (this.logger.isTraceEnabled()) {

  4. this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");

  5. }

  6. txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());

  7. }

  8. }

同样的,这里调用transactionManager进行提交

org.springframework.transaction.support.AbstractPlatformTransactionManager#commit

 
  1. public final void commit(TransactionStatus status) throws TransactionException {

  2. if (status.isCompleted()) {

  3. throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");

  4. } else {

  5. DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;

  6. if (defStatus.isLocalRollbackOnly()) {

  7. if (defStatus.isDebug()) {

  8. this.logger.debug("Transactional code has requested rollback");

  9. }

  10. this.processRollback(defStatus, false);

  11. } else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {

  12. if (defStatus.isDebug()) {

  13. this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");

  14. }

  15. this.processRollback(defStatus, true);

  16. } else {

  17. this.processCommit(defStatus);

  18. }

  19. }

  20. }

这个方法判断了一些无法提交的情况,程序这里走第二个分支,部分代码如下:

 
  1. else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {

  2. if (defStatus.isDebug()) {

  3. this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");

  4. }

  5. this.processRollback(defStatus, true);

  6. }

判断条件为: 

1.全局不是rollbackonly的时候也提交(这个可能是一个配置的参数,配合在rollbackonly的时候也提交,也就是出现现在这种情况后,不用回滚,直接提交)   

2.并且全局是rollbackonly

org.springframework.transaction.support.DefaultTransactionStatus#isGlobalRollbackOnly

 
  1. public boolean isGlobalRollbackOnly() {

  2. return this.transaction instanceof SmartTransactionObject && ((SmartTransactionObject)this.transaction).isRollbackOnly();

  3. }

这里又要满足两个条件

1 .这里的transaction是DataSourceTransactionObject

DataSourceTransaction继承 JdbcTransactionObjectSupport

JdbcTransactionObjectSupport又实现 SmartTransactionObject,所以第一个条件满足

2. DatSourceTransactionObject的 RollbackOnly 的get和set方法如下

 
  1. public void setRollbackOnly() {

  2. this.getConnectionHolder().setRollbackOnly();

  3. }

  4. public boolean isRollbackOnly() {

  5. return this.getConnectionHolder().isRollbackOnly();

  6. }

之前B方法抛出异常的时候,就是调用的DataSourceTransactionObject的set方法设置rollbackonly为true,现在再用get方法获取,只要是同一个connectionHolder,A获取到的rollbackOnly就是true,就会触发回滚,执行   this.processRollback(defStatus, true);

org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback

 
  1. private void processRollback(DefaultTransactionStatus status, boolean unexpected) {

  2. try {

  3. boolean unexpectedRollback = unexpected;

  4. try {

  5. this.triggerBeforeCompletion(status);

  6. if (status.hasSavepoint()) {

  7. if (status.isDebug()) {

  8. this.logger.debug("Rolling back transaction to savepoint");

  9. }

  10. status.rollbackToHeldSavepoint();

  11. } else if (status.isNewTransaction()) {

  12. if (status.isDebug()) {

  13. this.logger.debug("Initiating transaction rollback");

  14. }

  15. this.doRollback(status);

  16. } else {

  17. if (status.hasTransaction()) {

  18. if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {

  19. if (status.isDebug()) {

  20. this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback");

  21. }

  22. } else {

  23. if (status.isDebug()) {

  24. this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");

  25. }

  26. this.doSetRollbackOnly(status);

  27. }

  28. } else {

  29. this.logger.debug("Should roll back transaction but cannot - no transaction available");

  30. }

  31. if (!this.isFailEarlyOnGlobalRollbackOnly()) {

  32. unexpectedRollback = false;

  33. }

  34. }

  35. } catch (Error | RuntimeException var8) {

  36. this.triggerAfterCompletion(status, 2);

  37. throw var8;

  38. }

  39. this.triggerAfterCompletion(status, 1);

  40. if (unexpectedRollback) {

  41. throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");

  42. }

  43. } finally {

  44. this.cleanupAfterCompletion(status);

  45. }

  46. }

到这里  unexpectedRollback为true,就抛出了  "Transaction rolled back because it has been marked as rollback-only"这个异常了。

解决方案
原场景,内层事务的异常被外层事务捕获,内层被标记rollback,而外层提交,最后事务提交校验时抛出Transaction rolled back because it has been marked as rollback-only异常

@Transactional(rollbackFor = Exception.class)
public void methodA(){
    insert();
    System.out.println(1 / 0);
}

@Transactional(rollbackFor = Exception.class)
public void methodB(){
    try{
        methodA();
    }catch(Exception e){
        System.out.println("有异常");
    }
}



可以分两种情况来解决这个异常
如果我们需要内层异常的情况下,回滚整个事务,可以让内层事务抛出的异常被外层事务的try----catch处理,再抛出新的异常,或者外层不通过try—catch处理这个异常。
当然如果内层事务没有复用过,只是在这个地方使用,直接把内层的事务去了,让他和外层合并成一个事务也能解决这个问题。

@Transactional(rollbackFor = Exception.class)
public void methodA(){
    System.out.println(1 / 0);
}

@Transactional(rollbackFor = Exception.class)
public void methodB(){
    try{
        insert();
        methodA();
    }catch(Exception e){
        throw new Exception("存在异常")
    }
}



如果内层事务异常的情况下只回滚内层事务,修改内层事务的事务传播方式

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void methodA(){
    System.out.println(1 / 0);
}

@Transactional(rollbackFor = Exception.class)
public void methodB(){
    try{
        insert();
        methodA();
    }catch(Exception e){
        System.out.println("有异常");
    }
}


 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐