關於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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM