spring事务报错Transaction rolled back because it has been marked as rollback-only

news2024/12/24 9:03:27

之前经常报"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("有异常");
    }
}


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/514855.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

套接字类型和协议设置

创建套接字 成功返回文件描述符&#xff0c;失败返回-1 int socket (int __domain, int __type, int __protocol) ;__domain&#xff1a;套接字中使用的协议族信息 一般使用PF_INET&#xff08;IPv4互联网协议族&#xff09;&#xff0c;其它协议族不常使用或尚未普及。另外&a…

算法设计与分析:大整数的加减乘除运算

目录 任务描述 相关知识 大整数的思想 大整数加法 大整数减法 大整数与整数的乘法 大整数乘法 大整数与整数的除法 n的阶乘求解思路 编程要求 测试说明 任务描述 本关任务&#xff1a;掌握大整数的基本思想&#xff0c;并运用大整数的基本运算计算出常规整数n的阶乘…

seaweedfs服务启动参数及翻译(seaweed参数、seaweed命令、weed参数、weed命令)(在单个容器同时启动master、volume、filer服务)

文章目录 weed命令翻译weed server命令翻译 weed filer命令 docker容器运行示例&#xff08;docker run命令&#xff09;以下是一个在单个容器启动master、volume、filer服务的示例 weed server 容器debug 版本&#xff1a;3.47从官方镜像docker容器里扒下来的&#xff0c;只扒…

【多线程】单例模式

目录 饿汉模式 懒汉模式-单线程版 懒汉模式-多线程版 懒汉模式-多线程版(改进) 单例是一种设计模式。 啥是设计模式 ? 设计模式好比象棋中的 " 棋谱 ". 红方当头炮 , 黑方马来跳 . 针对红方的一些走法 , 黑方应招的时候有一些固定的套路. 按照套路来走局势…

5.11 C高级作业

编写一个名为myfirstshell.sh的脚本&#xff0c;它包括以下内容。 1、包含一段注释&#xff0c;列出您的姓名、脚本的名称和编写这个脚本的目的 2、和当前用户说“hello 用户名” 3、显示您的机器名 hostname 4、显示上一级目录中的所有文件的列表 5、显示变量PATH和HOME的…

高性能网络 SIG 月度动态:长期投入得到业界认可,新增一位 virtio reviewer

高性能网络 SIG&#xff08;Special Interest Group&#xff09; &#xff1a;在云计算时代&#xff0c;软硬件高速发展&#xff0c;云原生、微服务等新的应用形态兴起&#xff0c;让更多的数据在进程之间流动&#xff0c;而网络则成为了这些数据流的载体&#xff0c;在整个云时…

Hive概述和安装

hive简介 Hive&#xff1a;由Facebook开源用于解决海量结构化日志的数据统计工具。 Hive是基于Hadoop的一个数据仓库工具&#xff0c;将结构化的数据文件映射为一张表&#xff0c;并提供类SQL(HQL)查询功能。 Hive本质&#xff1a;将HQL转化成MapReduce程序 &#xff08;1&am…

无锁队列实现及使用场景

写在前面 在看无锁队列之前&#xff0c;我们先来看看看队列的操作。队列是一种非常重要的数据结构&#xff0c;其特性是先进先出&#xff08;FIFO&#xff09;&#xff0c;符合流水线业务流程。在进程间通信、网络通信间经常采用队列做缓存&#xff0c;缓解数据处理压力。根据…

device_node转换成platform_device

device_node转换成platform_device 文章目录 device_node转换成platform_device转换规则主要核心函数of_default_bus_match_tablearmarm64of_platform_register_reconfig_notifier Linux内核是如何将device_node转换成platform_deviceof_platform_populate函数处理根节点下的子…

在ubuntu连接Xlight FTP Server

一 在windows上搭建服务器 http://www.xlightftpd.com/download.htm 使用英文版&#xff0c;使防止在ubuntu中登录中文版时&#xff0c;显示乱码 新建用户和用户对应的服务器目录 如下所示&#xff0c;默认只有读权限 全都勾选 勾选完毕后的效果 在目录中放一个文件&#…

10款常用的原型设计工具,包含一键生成原型工具

原型图是产品设计师日常工作的“常客”&#xff0c;原型图软件也扮演着产品设计师的“武器”角色。 许多新产品设计师不知道如何选择原型图软件。本文盘点了10个优秀的原型图软件&#xff0c;让我们来看看。 1.即时设计 即时设计是一款免费的在线 UI 设计工具&#xff0c;无…

【Java数据结构】排序

排序 插入排序希尔排序选择排序堆排序冒泡排序快速排序序列的分割Hoare法挖坑法快慢指针法 优化1 - 三数取中优化2- 数据规模小时的插入 归并排序 插入排序 直接插入排序是一种简单的插入排序法&#xff0c;其基本思想是&#xff1a; 把待排序的记录按其关键码值的大小逐个插…

Jetson Orin环境安装Opencv+Cuda以及vscode环境配置

文章目录 一&#xff1a;Opencv Cuda源码的下载、编译1.卸载jetson上自带的无cuda加速Opencv2.安装Opencv依赖库3.下载 OpenCV 和 opencv_contrib 源码4.编译安装 OpenCV、opencv_contrib 二&#xff1a;Opencv 的环境配置三&#xff1a;Vscode 中的Opencv环境配置四&#xff…

系统分析师---系统建模相关高频考试知识点

系统规划---成本效益分析 评价信息系统经济效益常用的方法主要有成本效益分析法,投入产出分析法和价值工程方法。盈亏平衡法常用于销售定价; 可行性分析 系统规划是信息系统生命周期的第一个阶段,其任务是对企业的环境、目标以及现有系统的状况进行初步调查,根据企业目标…

张正友相机标定原理

相机标定 记录1.1 张正友相机标定相关 参考 记录 最小二乘法&#xff1a;A^T A x 0 奇异值分解的办法求解最小二乘法 因为可以假设标定板平面在世界坐标系Z0的平面上&#xff0c; 1.1 张正友相机标定相关 单目相机标定实现–张正友标定法(包含具体的实现以及C代码&#xff0…

《花雕学AI》ChatGPT Shortcut Chrome 扩展:让生产力和创造力加倍的 ChatGPT 快捷指令库

你是否想要与一个智能的对话伙伴聊天&#xff0c;或者让它帮你完成各种任务&#xff0c;如写作、编程、摘要、翻译等&#xff1f;如果是的话&#xff0c;你可能会对 ChatGPT 感兴趣。ChatGPT 是一个基于 GPT-3.5 的对话式人工智能&#xff0c;可以与用户进行自然、流畅、有趣的…

文件看不见了,内存还占着容量的找回教程

U盘文件突然不见了但还占用内存空间的解决方法 如果文件看不见了但内存占用仍然存在&#xff0c;可能是因为以下原因&#xff1a; 文件被隐藏。某些操作系统允许隐藏文件&#xff0c;这些文件只能在文件浏览器中被找到。 文件被损坏。如果文件损坏&#xff0c;它可能不会显示在…

Python图形化编程开源项目拼码狮PinMaShi

开源仓库 #项目地址 https://github.com/supercoderlee/pinmashi https://gitee.com/supercoderlee/pinmashiPinMaShi采用electron开发&#xff0c;图形化拖拽式编程有效降低编程难度&#xff0c;对Python编程的初学者非常友好&#xff1b;积木式编程加快Python程序的开发&…

黑马Redis笔记-高级篇

黑马Redis笔记-高级篇 1、Redis持久化&#xff08;解决数据丢失&#xff09;1.1 RDB持久化1.1.1 定义1.1.2 异步持久化bgsave原理 1.2 AOF持久化1.3 RDB和AOF比较 2、Redis主从&#xff08;解决并发问题&#xff09;2.1 搭建主从架构2.2 主从数据同步原理2.2.1 全量同步2.2.2 增…

基于哈里斯鹰算法优化的核极限学习机(KELM)分类算法 -附代码

基于哈里斯鹰算法优化的核极限学习机(KELM)分类算法 文章目录 基于哈里斯鹰算法优化的核极限学习机(KELM)分类算法1.KELM理论基础2.分类问题3.基于哈里斯鹰算法优化的KELM4.测试结果5.Matlab代码 摘要&#xff1a;本文利用哈里斯鹰算法对核极限学习机(KELM)进行优化&#xff0c…