zoukankan      html  css  js  c++  java
  • 关于Spring事务管理 之 默认事务间调用问题

    事务概念略;

    由事务的传播行为我们知道, 如果将方法配置为默认事务(REQUIRED)在执行过程中Spring会为其新启事务(REQUIRES_NEW), 作为一个独立事务来执行. 由此存在一个问题.

    如果使用不慎, 会引发 org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only


    具体原因见以下demo简例:

    部分关键代码

    DemoService1
    public class DemoService1Impl implements DemoService1 {
    
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
        @Resource
        private DemoDao demoDao;
        @Resource
        private DemoService2 demoService2;
    
        /**
         * 业务逻辑 , 默认事务, 事务回滚异常 : Exception
         */
        @Override
        @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
        public void doService() {
            HashMap<String, Integer> param = new HashMap<>(2);
            param.put("applId", 19);
            param.put("code", 19);
            demoDao.insert1(param);
            try {
                demoService2.doService();
            } catch (Exception e) {
                logger.error("业务2处理异常,{}", e.getMessage());
            }
    
        }
    }
    
    DemoService2
    public class DemoService2Impl implements DemoService2 {
    
        @Resource
        private DemoDao demoDao;
    
        /**
         * 业务逻辑, 默认事务, 事务回滚异常 : Exception
         */
        @Override
        @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
        public void doService() {
            HashMap<String, Integer> param = new HashMap<>(2);
            param.put("applId", 10);
            param.put("code", 10);
            demoDao.insert2(param);
            throw new RuntimeException("因为一些原因,我处理失败了.");
        }
    }
    
    

    单元测试

    public class DemoService1ImplTest extends BaseTest {
    
        @Resource
        private DemoService1 demoService1;
    
        @Test
        public void doService() {
            demoService1.doService();
        }
    }
    
    说明

    这里用到的事务配置为注解方式, 目前我们项目开发过程中使用配置文件方式, 一般为以下方式 . 这种方式的事务配置, 更容易引起问题

    	<tx:advice id="txAdvice" transaction-manager="transactionManager">
    		<tx:attributes>
            	...
    			<tx:method name="do*"  />
                <tx:method name="doNew*"  propagation="REQUIRES_NEW" />
                ...
    		</tx:attributes>
    	</tx:advice>
    
    

    执行结果

    27:38 [DEBUG] - [com.erayt.cms.cms.dao.DemoDao.insert1] prepare sql:[         insert into  ...
    27:38 [DEBUG] - [com.erayt.cms.cms.dao.DemoDao.insert1] prepare parameters:[19, 19]  ...
    27:38 [DEBUG] - {pstm-100001} Executing Statement:          insert into   ...
    27:38 [DEBUG] - {pstm-100001} Types: [java.lang.Integer, java.lang.Integer]  ...
    27:38 [DEBUG] - [com.erayt.cms.cms.dao.DemoDao.insert2] prepare sql:[         insert into   ...
    27:38 [DEBUG] - [com.erayt.cms.cms.dao.DemoDao.insert2] prepare parameters:[10, 10]  ...
    27:38 [DEBUG] - {conn-100002} Preparing Statement:          insert into   ...
    27:38 [DEBUG] - {pstm-100003} Types: [java.lang.Integer, java.lang.Integer]  ...
    27:38 [ERROR] - 业务2处理异常,因为一些原因,我处理失败了.
    27:38 [WARN ] - Caught exception while allowing TestExecutionListener  ...
    org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
    	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit ...
    	at org.springframework.test.context.transaction.TransactionContext.endTransaction ...
    	at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod ...
    	at org.springframework.test.context.TestContextManager.afterTestMethod ...
    	
    
    

    问题分析

    问题出现的代码为

    	try {
                demoService2.doService();
            } catch (Exception e) {
                logger.error("业务2处理异常,{}", e.getMessage());
            }
    

    问题原因是因为两个service中的方法doService均为默认事务(REQUIRED),
    默认事务再被调用时, 如外层方法无事务, 自身会新启事务. 此时#demoService1.doService()的事务则为新启事务(REQUIRES_NEW) , 之后再被调用的方法#demoService2.doService()会加入到调用者#demoService1.doService()事务中.

    又由于spring的事务回滚依托在异常之上, 当demoService2.doService()出现异常后它将事务标记为回滚. 异常抛出后被catch , demoService1.doService 没有接受到里面抛出的异常, 方法继续执行, 执行结束后, 事务提交.

    但当demoService1在做commit的时候检测到事务被标记为回滚, 与预期不符, 也就是Unexpected 意想不到的

    UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

  • 相关阅读:
    app测试点-1
    毕业5年的感悟
    关于游戏外挂
    python-unittest单元测试框架
    python-requests
    http简介
    python基础-发邮件smtp
    python-加密
    4 Python 日期和时间
    5 Python 数据类型—数字
  • 原文地址:https://www.cnblogs.com/lvgo/p/13275809.html
Copyright © 2011-2022 走看看