• 关于spring 事物传播性的研究


    spring的一大特色就是数据库事务管理方便,我们在代码中编写代码时,看不到事务的使用,关键是spring 使用了AOP进行事务拦截。

     

    这篇文章主要介绍spring的事务传播性。

     

    1.为什么要介绍这个:

    介绍一下使用场景:

    在系统中我们使用了一个流水号,这个功能实现如下:

    1.先使用for update 进行行锁。

    select * from sys_identity t where alias='REQUESTID' for update;

    这个时候锁是不会释放的。

    2.使用更新语句更新这个流水号。

    UPDATE sys_identity SET
            name=#{name,jdbcType=VARCHAR} , 
            alias=#{alias,jdbcType=VARCHAR} , 
            REGULATION=#{rule,jdbcType=VARCHAR} , 
            genType=#{genType,jdbcType=NUMERIC} , 
            noLength=#{noLength,jdbcType=NUMERIC} , 
            initValue=#{initValue,jdbcType=NUMERIC} , 
            curValue=#{curValue,jdbcType=NUMERIC} , 
            step=#{step,jdbcType=NUMERIC} , 
            curDate=#{curDate,jdbcType=VARCHAR} 
            WHERE
            id=#{id}

    执行完成后,就会释放之前的锁定。

     

    这个功能在单独调用的时候,是没有问题的,但是如果这个获取流水号的方法,放到一个长的事务中,势必这个就成了瓶颈。

    应为长事务方法会一直锁住这个,不会提交。

     

    2.解决方案:

    如果我们在长事务方法中,能够将这个获取流水号的方法新起一个事务,这样这个方法很快就会提交。这个时候我们就可以利用事务的传播

    性来解决这个问题。

     

    下面介绍一下事务传播性概念:

    PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启
    PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
    PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
    PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
    PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
    PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
    PROPAGATION_NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务,
          则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
    在默认的情况下,我们采用的是

    PROPAGATION_REQUIRED

    举例如下:

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                  <tx:method name="get*" read-only="true"/>
                <tx:method name="is*" read-only="true"/>
                <tx:method name="find*" read-only="true"/>
                <tx:method name="*" isolation="DEFAULT"/>
            </tx:attributes>
    </tx:advice>

    这种情况下,只要被拦截的业务方法抛出异常都会回滚。

     

    如果我想实现上面的需求,我们修改配置如下:

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="nextId" propagation="REQUIRES_NEW" />
                  <tx:method name="get*" read-only="true"/>
                <tx:method name="is*" read-only="true"/>
                <tx:method name="find*" read-only="true"/>
                <tx:method name="*" isolation="DEFAULT"/>
            </tx:attributes>
    </tx:advice>

    这里我加入了一个方法进行单独配置:

    方法名为nextId ,这里我们配置了REQUIRES_NEW。

    预期的目标是,让这个方法产生新的事务。

     

    3.测试代码如下

    @Service
    public class ServiceExample {
    
        @Resource
        WorkPlandetailDao workPlandetailDao;
        @Resource
        IdentityService identityService;
        
        public void updWork(){
            WorkPlandetail plandetail=new WorkPlandetail();
            plandetail.setId(UniqueIdUtil.genId());
            plandetail.setDocids(1L);
            plandetail.setRq(new Date());
            plandetail.setUserid(1L);
            
            workPlandetailDao.add(plandetail);
            
            identityService.nextId("REQUESTID");
            boolean rtn=true;
            if(rtn){
                throw new RuntimeException("抛出异常");
            }
        }
    }

    这个代码如果成立结果应该是这样

    1.第一个添加应该回滚。

    2.流水号产生不回滚。

     

    4.详细日志如下:

    事务启动

    [BPM] 2015-01-22 14:37:27 DEBUG [main] AnnotationTransactionAttributeSource.getTransactionAttribute(106) | Adding transactional method 'transTest' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
    [BPM] 2015-01-22 14:37:27 DEBUG [main] DataSourceTransactionManager.getTransaction(365) | Creating new transaction with name [transTest]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
    [BPM] 2015-01-22 14:37:27 DEBUG [main] DataSourceTransactionManager.doBegin(204) | Acquired Connection [1940809161(oracle.jdbc.driver.T4CConnection@c65d26e)] for JDBC transaction
    [BPM] 2015-01-22 14:37:27 DEBUG [main] DataSourceTransactionManager.doBegin(221) | Switching JDBC Connection [1940809161(oracle.jdbc.driver.T4CConnection@c65d26e)] to manual commit


    [BPM] 2015-01-22 14:37:49 DEBUG [main] DataSourceTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
    [BPM] 2015-01-22 14:37:52 DEBUG [main] JdbcTemplate.query(634) | Executing prepared SQL query
    [BPM] 2015-01-22 14:37:52 DEBUG [main] JdbcTemplate.execute(569) | Executing prepared SQL statement [SELECT bound,incremental FROM SYS_DB_ID T WHERE T.ID=?]
    [BPM] 2015-01-22 14:37:52 DEBUG [main] DataSourceUtils.doGetConnection(110) | Fetching JDBC Connection from DataSource
    [BPM] 2015-01-22 14:37:53 DEBUG [main] DataSourceUtils.doGetConnection(114) | Registering transaction synchronization for JDBC Connection
    [BPM] 2015-01-22 14:37:53 DEBUG [main] JdbcTemplate.update(810) | Executing prepared SQL update
    [BPM] 2015-01-22 14:37:53 DEBUG [main] JdbcTemplate.execute(569) | Executing prepared SQL statement [UPDATE SYS_DB_ID  SET BOUND=? WHERE ID=?]
    [BPM] 2015-01-22 14:37:53 DEBUG [main] JdbcTemplate.doInPreparedStatement(819) | SQL update affected 1 rows
    [BPM] 2015-01-22 14:37:54 DEBUG [main] Connection.debug(28) | ooo Connection Opened
    [BPM] 2015-01-22 14:37:54 DEBUG [main] PreparedStatement.debug(28) | ==>  Executing: INSERT INTO WORKPLANDETAIL (ID,WORKID,USERID,DOCIDS,RQ,TIME) VALUES (?, ?, ?, ?, ?, ?)
    [BPM] 2015-01-22 14:37:54 DEBUG [main] PreparedStatement.debug(28) | ==> Parameters: 10000027280000(Long), null, 1(Long), 1(Long), 2015-01-22(Date), null
    [BPM] 2015-01-22 14:37:54 DEBUG [main] Connection.debug(28) | xxx Connection Closed

    挂起当前事务,新起一个事务。
    [BPM] 2015-01-22 14:37:58 DEBUG [main] DataSourceTransactionManager.handleExistingTransaction(415) | Suspending current transaction, creating new transaction with name [com.hotent.platform.service.system.IdentityService.nextId]
    [BPM] 2015-01-22 14:37:58 DEBUG [main] DataSourceUtils.doReleaseConnection(332) | Returning JDBC Connection to DataSource
    [BPM] 2015-01-22 14:37:58 DEBUG [main] DataSourceTransactionManager.doBegin(204) | Acquired Connection [1210980916(oracle.jdbc.driver.T4CConnection@1a78b825)] for JDBC transaction
    [BPM] 2015-01-22 14:37:58 DEBUG [main] DataSourceTransactionManager.doBegin(221) | Switching JDBC Connection [1210980916(oracle.jdbc.driver.T4CConnection@1a78b825)] to manual commit
    [BPM] 2015-01-22 14:37:58 DEBUG [main] Connection.debug(28) | ooo Connection Opened
    [BPM] 2015-01-22 14:37:58 DEBUG [main] PreparedStatement.debug(28) | ==>  Executing: SELECT id,name,alias,REGULATION,genType,noLength,initValue,curValue,step,curDate FROM SYS_IDENTITY WHERE alias=? FOR UPDATE
    [BPM] 2015-01-22 14:37:58 DEBUG [main] PreparedStatement.debug(28) | ==> Parameters: REQUESTID(String)
    [BPM] 2015-01-22 14:37:58 DEBUG [main] Connection.debug(28) | xxx Connection Closed
    [BPM] 2015-01-22 14:37:58 DEBUG [main] Connection.debug(28) | ooo Connection Opened
    [BPM] 2015-01-22 14:37:58 DEBUG [main] PreparedStatement.debug(28) | ==>  Executing: UPDATE sys_identity SET name=? , alias=? , REGULATION=? , genType=? , noLength=? , initValue=? , curValue=? , step=? , curDate=? WHERE id=?
    [BPM] 2015-01-22 14:37:58 DEBUG [main] PreparedStatement.debug(28) | ==> Parameters: 工号(String), REQUESTID(String), {NO}(String), 0(Short), 1(Integer), 60000(Integer), 90191(Integer), 1(Short), 2013520(String), 10000006120000(Long)
    [BPM] 2015-01-22 14:37:58 DEBUG [main] Connection.debug(28) | xxx Connection Closed
    [BPM] 2015-01-22 14:37:58 DEBUG [main] DataSourceTransactionManager.processCommit(752) | Initiating transaction commit
    [BPM] 2015-01-22 14:37:58 DEBUG [main] DataSourceTransactionManager.doCommit(264) | Committing JDBC transaction on Connection [1210980916(oracle.jdbc.driver.T4CConnection@1a78b825)]

    提交流水号事务
    [BPM] 2015-01-22 14:37:58 DEBUG [main] DataSourceTransactionManager.doCleanupAfterCompletion(322) | Releasing JDBC Connection [1210980916(oracle.jdbc.driver.T4CConnection@1a78b825)] after transaction
    [BPM] 2015-01-22 14:37:58 DEBUG [main] DataSourceUtils.doReleaseConnection(332) | Returning JDBC Connection to DataSource
    [BPM] 2015-01-22 14:37:58 DEBUG [main] DataSourceTransactionManager.cleanupAfterCompletion(1015) | Resuming suspended transaction after completion of inner transaction

    恢复当前事务
    [BPM] 2015-01-22 14:38:16 DEBUG [main] DataSourceTransactionManager.processRollback(850) | Participating transaction failed - marking existing transaction as rollback-only
    [BPM] 2015-01-22 14:38:16 DEBUG [main] DataSourceTransactionManager.doSetRollbackOnly(293) | Setting JDBC transaction [1940809161(oracle.jdbc.driver.T4CConnection@c65d26e)] rollback-only
    [BPM] 2015-01-22 14:38:16 DEBUG [main] DataSourceTransactionManager.commit(711) | Global transaction is marked as rollback-only but transactional code requested commit
    [BPM] 2015-01-22 14:38:16 DEBUG [main] DataSourceTransactionManager.processRollback(843) | Initiating transaction rollback
    [BPM] 2015-01-22 14:38:16 DEBUG [main] DataSourceTransactionManager.doRollback(279) | Rolling back JDBC transaction on Connection [1940809161(oracle.jdbc.driver.T4CConnection@c65d26e)]

    事务回滚
    [BPM] 2015-01-22 14:38:16 DEBUG [main] DataSourceTransactionManager.doCleanupAfterCompletion(322) | Releasing JDBC Connection [1940809161(oracle.jdbc.driver.T4CConnection@c65d26e)] after transaction
    [BPM] 2015-01-22 14:38:16 DEBUG [main] DataSourceUtils.doReleaseConnection(332) | Returning JDBC Connection to DataSource
    [BPM] 2015-01-22 14:38:16 DEBUG [ShutdownHook] ShutdownHook.run(93) | Running ShutdownHook
    [BPM] 2015-01-22 14:38:16 INFO [Shutdown Hook] dbpool.shutdown(484) | Shutting down 'dbpool' pool immediately [Shutdown Hook]
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServerImpl.stop(163) | Server is stopping.
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServletDestination.setMessageObserver(56) | unregistering incoming observer: org.apache.cxf.transport.ChainInitiationObserver@2d4e7451
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServerImpl.destroy(185) | unregister the server to serverRegistry
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServerImpl.stop(163) | Server is stopping.
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServletDestination.setMessageObserver(56) | unregistering incoming observer: org.apache.cxf.transport.ChainInitiationObserver@2ad22562
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServerImpl.destroy(185) | unregister the server to serverRegistry
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServerImpl.stop(163) | Server is stopping.
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServletDestination.setMessageObserver(56) | unregistering incoming observer: org.apache.cxf.transport.ChainInitiationObserver@5c894b4f
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServerImpl.destroy(185) | unregister the server to serverRegistry
    [BPM] 2015-01-22 14:38:16 DEBUG [Shutdown Hook] dbpool.removeProxyConnection(441) | 000006 (00/02/00) - #0003 removed because of shutdown.
    [BPM] 2015-01-22 14:38:16 DEBUG [Shutdown Hook] dbpool.shutdown(547) | Connection #3 closed
    [BPM] 2015-01-22 14:38:16 DEBUG [Shutdown Hook] dbpool.removeProxyConnection(441) | 000006 (00/01/00) - #0001 removed because of shutdown.
    [BPM] 2015-01-22 14:38:16 DEBUG [Shutdown Hook] dbpool.shutdown(547) | Connection #1 closed
    [BPM] 2015-01-22 14:38:16 DEBUG [Shutdown Hook] dbpool.removeProxyConnection(441) | 000006 (00/00/00) - #0002 removed because of shutdown.
    [BPM] 2015-01-22 14:38:16 DEBUG [Shutdown Hook] dbpool.shutdown(547) | Connection #2 closed
    [BPM] 2015-01-22 14:38:16 INFO [Shutdown Hook] dbpool.shutdown(564) | 'dbpool' pool has been closed down by Shutdown Hook in 30 milliseconds.
    [BPM] 2015-01-22 14:38:16 INFO [Shutdown Hook] PrototyperController.shutdown(100) | Stopping Prototyper thread
    [BPM] 2015-01-22 14:38:16 INFO [Shutdown Hook] HouseKeeperController.shutdown(107) | Stopping HouseKeeper thread
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServerImpl.destroy(185) | unregister the server to serverRegistry
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServerImpl.destroy(185) | unregister the server to serverRegistry
    [BPM] 2015-01-22 14:38:16 DEBUG [Thread-20] ServerImpl.destroy(185) | unregister the server to serverRegistry
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] DataSourceTransactionManager.getTransaction(365) | Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] HouseKeeperController.register(84) | Registering 'dbpool' house keeper
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] HouseKeeperController.register(92) | Starting a house keeper thread
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] dbpool.initialise(168) | Remembering default value: getTransactionIsolation() = 2
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] dbpool.initialise(168) | Remembering default value: getHoldability() = 1
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] dbpool.initialise(168) | Remembering default value: getCatalog() = null
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] dbpool.initialise(168) | Remembering default value: isReadOnly() = false
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] dbpool.initialise(168) | Remembering default value: getTypeMap() = {}
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] dbpool.buildConnection(204) | 000000 (01/01/00) - Connection #1 created on demand = ACTIVE
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] DataSourceTransactionManager.doBegin(204) | Acquired Connection [741406706(oracle.jdbc.driver.T4CConnection@63cffc5f)] for JDBC transaction
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] DataSourceTransactionManager.doBegin(221) | Switching JDBC Connection [741406706(oracle.jdbc.driver.T4CConnection@63cffc5f)] to manual commit
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] Connection.debug(28) | ooo Connection Opened
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] Connection.debug(28) | xxx Connection Closed
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] DataSourceTransactionManager.processCommit(752) | Initiating transaction commit
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] DataSourceTransactionManager.doCommit(264) | Committing JDBC transaction on Connection [741406706(oracle.jdbc.driver.T4CConnection@63cffc5f)]
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] DataSourceTransactionManager.doCleanupAfterCompletion(322) | Releasing JDBC Connection [741406706(oracle.jdbc.driver.T4CConnection@63cffc5f)] after transaction
    [BPM] 2015-01-22 14:38:17 DEBUG [Thread-20] DataSourceUtils.doReleaseConnection(332) | Returning JDBC Connection to DataSource
    [BPM] 2015-01-22 14:38:17 DEBUG [Prototyper] dbpool.buildConnection(204) | 000001 (00/02/00) - Connection #2 created to achieve minimum of 3 = AVAILABLE
    [BPM] 2015-01-22 14:38:17 DEBUG [Prototyper] dbpool.buildConnection(204) | 000001 (00/03/00) - Connection #3 created to achieve minimum of 3 = AVAILABLE

  • 相关阅读:
    C#中的接口和类的不同点
    值类型和引用类型的区别?
    时隔两年再次操刀NPOI合并单元格
    二.Docker下安装和运行Mysql
    一.CentOS8下的Docker安装
    .NetCore3.1使用Autofac
    .NET Core 3.1使用Swagger
    数组排序和数组对象排序
    C# 操作Excel , 支持超链接 跳转Sheet 页面,HSSFHyperlink函数
    MVC导入Excel通过NPOI
  • 原文地址:https://www.cnblogs.com/yg_zhang/p/4241769.html
Copyright © 2020-2023  润新知