Spring事務傳播實驗剖析(一)

摘抄概念以下:java

Spring在TransactionDefinition接口中規定了7種類型的事務傳播行爲,它們規定了事務方法和事務方法發生嵌套調用時事務如何進行傳播:web

表1事務傳播行爲類型spring

事務傳播行爲類型apache

說明session

PROPAGATION_REQUIREDmybatis

若是當前沒有事務,就新建一個事務,若是已經存在一個事務中,加入到這個事務中。這是最多見的選擇。spring默認使用此傳播類型mvc

PROPAGATION_SUPPORTSapp

支持當前事務,若是當前沒有事務,就以非事務方式執行。ide

PROPAGATION_MANDATORY測試

使用當前的事務,若是當前沒有事務,就拋出異常。

PROPAGATION_REQUIRES_NEW

新建事務,若是當前存在事務,把當前事務掛起。

PROPAGATION_NOT_SUPPORTED

以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。

PROPAGATION_NEVER

以非事務方式執行,若是當前存在事務,則拋出異常。

PROPAGATION_NESTED

若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與PROPAGATION_REQUIRED相似的操做。

當使用PROPAGATION_NESTED時,底層的數據源必須基於JDBC 3.0,而且實現者須要支持保存點事務機制

Spring 默認的事務傳播行爲是 PROPAGATION_REQUIRED,它適合於絕大多數的狀況。假設 ServiveX#methodX() 都工做在事務環境下(即都被 Spring 事務加強了),假設程序中存在以下的調用鏈:Service1#method1()->Service2#method2()->Service3#method3(),那麼這 3 個服務類的 3 個方法經過 Spring 的事務傳播機制都工做在同一個事務中。

 

經過閱讀Spring3.X企業開發實戰書籍,回顧了一下事務傳播的相關知識點,並經過小實驗來加深印象。

實驗以下:

由controller調用service,且包含不一樣service之間調用,日誌啓用DEBUG級別

事務傳播方式採用默認:PROPAGATION_REQUIRED

隔離級別採用默認:

1:

@RequestMapping(value="/test")
public void insert() {
	System.out.println("---------spring mvc update---------");
	userService.update(111,"aaaazz");
	System.out.println("---------spring mvc all---------");
}

2:UserServiceImpl的update方法:

@Override
@Transactional
public void update(int id,String username) {
	System.out.println("---------UserServiceImpl updateAge start---------");
	updateAge(id);
	System.out.println("---------UserServiceImpl updateAge  end---------");
	
	System.out.println("---------scoreService updateScore start---------");
	scoreService.updateScore(id);
	System.out.println("---------scoreService updateScore end---------");

    // int i = 1 / 0; //exception測試註釋
}

3:ScopeService的updateScore方法:

@Override
@Transactional
public void updateScore(int id) {
	scoreDao.updateScore(id);
// int i = 1 / 0; //exception測試註釋
}

實例1:

UserServiceImpl#update和ScoreService#updateScore都開啓事務註解,輸出日誌:

---------spring mvc update---------
[DEBUG] 2018-05-21 15:20:31,931 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
  [DEBUG] 2018-05-21 15:20:31,938 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'transactionManager'
  [DEBUG] 2018-05-21 15:20:31,947 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:366)

Creating new transaction with name [com.paic.ssm.user.service.impl.UserServiceImpl.update]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
  [DEBUG] 2018-05-21 15:20:31,948 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:204)
Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@8de40e] for JDBC transaction
  [DEBUG] 2018-05-21 15:20:31,956 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:221)
Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@8de40e] to manual commit
  ---------UserServiceImpl updateAge start---------
15:20:31.965 [http-bio-8080-exec-1] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
15:20:31.973 [http-bio-8080-exec-1] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1e45c4f]
15:20:32.030 [http-bio-8080-exec-1] DEBUG o.m.s.t.SpringManagedTransaction - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@8de40e] will be managed by Spring
15:20:32.033 [http-bio-8080-exec-1] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@8de40e]
15:20:32.039 [http-bio-8080-exec-1] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ==>  Preparing: update user_t set age = ? 
15:20:32.182 [http-bio-8080-exec-1] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ==> Parameters: 111(Integer)
[DEBUG] 2018-05-21 15:20:32,186 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10010, pstmt-20000} enter cache
  15:20:32.187 [http-bio-8080-exec-1] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1e45c4f]
---------UserServiceImpl updateAge  end---------
---------scoreService updateScore start---------
[DEBUG] 2018-05-21 15:20:32,187 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'transactionManager'
  [DEBUG] 2018-05-21 15:20:32,188 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:471)

Participating in existing transaction
  15:20:32.188 [http-bio-8080-exec-1] DEBUG org.mybatis.spring.SqlSessionUtils - Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1e45c4f] from current transaction
15:20:32.189 [http-bio-8080-exec-1] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@8de40e]
15:20:32.189 [http-bio-8080-exec-1] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==>  Preparing: update score_t set score = ? 
15:20:32.189 [http-bio-8080-exec-1] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==> Parameters: 111(Integer)
[DEBUG] 2018-05-21 15:20:32,254 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10010, pstmt-20001} enter cache
  15:20:32.256 [http-bio-8080-exec-1] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1e45c4f]
---------scoreService updateScore end---------
[DEBUG] 2018-05-21 15:20:32,257 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:753)
Initiating transaction commit
  [DEBUG] 2018-05-21 15:20:32,258 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DataSourceTransactionManager.java:267)

Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@8de40e]
  15:20:32.321 [http-bio-8080-exec-1] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1e45c4f]
15:20:32.321 [http-bio-8080-exec-1] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1e45c4f]
[DEBUG] 2018-05-21 15:20:32,322 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager.java:325)
Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@8de40e] after transaction
  [DEBUG] 2018-05-21 15:20:32,324 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
  ---------spring mvc all---------

能夠看到,在userServcie執行update以前,spring建立了一個事務:

Creating new transaction with name。。。

執行完畢以後,再執行scoreService的updateScore以前,並未建立新的事務,而是參與到了已存在的事務中:

Participating in existing transaction。。。

最終提交此事務:

Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@8de40e]

說明,不一樣service之間調用時,若二者都存在事務,則後者不會自省建立事務,而是參與到以前的事務中

2:UserServiceImpl#update開啓事務,ScoreService#updateScore不開啓事務,輸出日誌:

---------spring mvc update---------
[DEBUG] 2018-05-21 15:21:32,255 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
  [DEBUG] 2018-05-21 15:21:32,263 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'transactionManager'
  [DEBUG] 2018-05-21 15:21:32,273 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:366)

  
  Creating new transaction with name [com.paic.ssm.user.service.impl.UserServiceImpl.update]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
  [DEBUG] 2018-05-21 15:21:32,273 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:204)
Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@9d935d] for JDBC transaction
  [DEBUG] 2018-05-21 15:21:32,280 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:221)
Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@9d935d] to manual commit
  ---------UserServiceImpl updateAge start---------
15:21:32.289 [http-bio-8080-exec-3] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
15:21:32.296 [http-bio-8080-exec-3] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d92da1]
15:21:32.354 [http-bio-8080-exec-3] DEBUG o.m.s.t.SpringManagedTransaction - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@9d935d] will be managed by Spring
15:21:32.356 [http-bio-8080-exec-3] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@9d935d]
15:21:32.361 [http-bio-8080-exec-3] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ==>  Preparing: update user_t set age = ? 
15:21:32.502 [http-bio-8080-exec-3] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ==> Parameters: 111(Integer)
[DEBUG] 2018-05-21 15:21:32,506 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10010, pstmt-20000} enter cache
  15:21:32.507 [http-bio-8080-exec-3] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d92da1]
---------UserServiceImpl updateAge  end---------
---------scoreService updateScore start---------
15:21:32.507 [http-bio-8080-exec-3] DEBUG org.mybatis.spring.SqlSessionUtils - Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d92da1] from current transaction
15:21:32.507 [http-bio-8080-exec-3] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@9d935d]
15:21:32.507 [http-bio-8080-exec-3] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==>  Preparing: update score_t set score = ? 
15:21:32.508 [http-bio-8080-exec-3] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==> Parameters: 111(Integer)
[DEBUG] 2018-05-21 15:21:32,586 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10010, pstmt-20001} enter cache
  15:21:32.586 [http-bio-8080-exec-3] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d92da1]
---------scoreService updateScore end---------
[DEBUG] 2018-05-21 15:21:32,587 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:753)
Initiating transaction commit
  [DEBUG] 2018-05-21 15:21:32,587 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DataSourceTransactionManager.java:267)

  
  Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@9d935d]
  15:21:32.680 [http-bio-8080-exec-3] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d92da1]
15:21:32.680 [http-bio-8080-exec-3] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d92da1]
[DEBUG] 2018-05-21 15:21:32,680 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager.java:325)
Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@9d935d] after transaction
  [DEBUG] 2018-05-21 15:21:32,681 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
  ---------spring mvc all---------

能夠看到,scoreService並未有事務建立和傳播的日誌,暫沒法判定其是否被事務做用

打開userService#update的異常測試代碼: int i = 1 / 0;

執行以後,後臺報錯,部分日誌:

17:18:57.650 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c43375]
---------UserServiceImpl updateAge  end---------
---------scoreService updateScore start---------
17:18:57.651 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c43375] from current transaction
17:18:57.651 [http-bio-8080-exec-6] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@d6b1dc]
17:18:57.651 [http-bio-8080-exec-6] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==>  Preparing: update score_t set score = ? 
17:18:57.651 [http-bio-8080-exec-6] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==> Parameters: 111(Integer)
[DEBUG] 2018-05-21 17:18:57,652 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10010, pstmt-20001} enter cache
  17:18:57.652 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c43375]


---------scoreService updateScore end---------
[DEBUG] 2018-05-21 17:18:57,653 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:844)


Initiating transaction rollback
  [DEBUG] 2018-05-21 17:18:57,653 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doRollback(DataSourceTransactionManager.java:282)
Rolling back JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@d6b1dc]
  17:18:57.827 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization rolling back SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c43375]
17:18:57.827 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c43375]
[DEBUG] 2018-05-21 17:18:57,829 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager.java:325)
Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@d6b1dc] after transaction
  [DEBUG] 2018-05-21 17:18:57,830 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
  [DEBUG] 2018-05-21 17:18:57,846 method:org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:132)
Resolving exception from handler [public void com.paic.ssm.login.controller.LoginController.insert()]: java.lang.ArithmeticException: / by zero
  [DEBUG] 2018-05-21 17:18:57,850 method:org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:132)
Resolving exception from handler [public void com.paic.ssm.login.controller.LoginController.insert()]: java.lang.ArithmeticException: / by zero
  [DEBUG] 2018-05-21 17:18:57,850 method:org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:132)
Resolving exception from handler [public void com.paic.ssm.login.controller.LoginController.insert()]: java.lang.ArithmeticException: / by zero
  [DEBUG] 2018-05-21 17:18:57,852 method:org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
Could not complete request
  java.lang.ArithmeticException: / by zero
	at com.paic.ssm.user.service.impl.UserServiceImpl.update(UserServiceImpl.java:49)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

能夠看到,在scoreService執行完畢以後,拋出了異常,日誌輸出了事務rollback,經查驗DB,數據並未修改,說明兩個service都被事務做用了,也即事務由userService傳播到了scoreService,此時和第一種狀況是同樣的,不管二者誰拋出異常,只要被初始調用捕獲,就會回滾了。

注意:若是scoreService的異常被其自己捕獲,那麼數據也不會回滾。。。

3:UserServiceImpl#update不開啓事務,ScoreService#updateScore開啓事務,輸出日誌:

---------spring mvc update---------
---------UserServiceImpl updateAge start---------
15:27:15.292 [http-bio-8080-exec-8] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
15:27:15.298 [http-bio-8080-exec-8] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@76823b] was not registered for synchronization because synchronization is not active
[DEBUG] 2018-05-21 15:27:15,356 method:org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:110)


Fetching JDBC Connection from DataSource
  15:27:15.356 [http-bio-8080-exec-8] DEBUG o.m.s.t.SpringManagedTransaction - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@c824d8] will not be managed by Spring
15:27:15.358 [http-bio-8080-exec-8] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@c824d8]
15:27:15.361 [http-bio-8080-exec-8] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ==>  Preparing: update user_t set age = ? 
15:27:15.468 [http-bio-8080-exec-8] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ==> Parameters: 111(Integer)
[DEBUG] 2018-05-21 15:27:15,505 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10010, pstmt-20000} enter cache
  15:27:15.506 [http-bio-8080-exec-8] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@76823b]
[DEBUG] 2018-05-21 15:27:15,506 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
  ---------UserServiceImpl updateAge  end---------
---------scoreService updateScore start---------
[DEBUG] 2018-05-21 15:27:15,508 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
  [DEBUG] 2018-05-21 15:27:15,521 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'transactionManager'
  [DEBUG] 2018-05-21 15:27:15,526 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:366)

  
  Creating new transaction with name [com.paic.ssm.user.service.impl.ScoreServiceImpl.updateScore]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
  [DEBUG] 2018-05-21 15:27:15,527 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:204)
Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@c824d8] for JDBC transaction
  [DEBUG] 2018-05-21 15:27:15,532 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:221)
Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@c824d8] to manual commit
  15:27:15.534 [http-bio-8080-exec-8] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
15:27:15.534 [http-bio-8080-exec-8] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1d52634]
15:27:15.536 [http-bio-8080-exec-8] DEBUG o.m.s.t.SpringManagedTransaction - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@c824d8] will be managed by Spring
15:27:15.536 [http-bio-8080-exec-8] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@c824d8]
15:27:15.536 [http-bio-8080-exec-8] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==>  Preparing: update score_t set score = ? 
15:27:15.537 [http-bio-8080-exec-8] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==> Parameters: 111(Integer)
[DEBUG] 2018-05-21 15:27:15,537 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10010, pstmt-20001} enter cache
  15:27:15.538 [http-bio-8080-exec-8] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1d52634]
[DEBUG] 2018-05-21 15:27:15,538 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:753)
Initiating transaction commit
  [DEBUG] 2018-05-21 15:27:15,538 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DataSourceTransactionManager.java:267)

  
  Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@c824d8]
  15:27:15.622 [http-bio-8080-exec-8] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1d52634]
15:27:15.623 [http-bio-8080-exec-8] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1d52634]
[DEBUG] 2018-05-21 15:27:15,624 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager.java:325)
Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@c824d8] after transaction
  [DEBUG] 2018-05-21 15:27:15,625 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
  ---------scoreService updateScore end---------
---------spring mvc all---------

能夠看到,userService先執行update,可是並未建立事務,而是直接獲取到了connection:

Fetching JDBC Connection from DataSource。。。

而scoreService則開啓了事務,並正常commit

有什麼影響呢?下面再次打開userService的異常測試代碼,執行日誌以下:

Initiating transaction commit
  [DEBUG] 2018-05-21 17:32:08,784 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DataSourceTransactionManager.java:267)


Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@53ba89]
  17:32:08.853 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@45ce57]
17:32:08.853 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@45ce57]
[DEBUG] 2018-05-21 17:32:08,854 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager.java:325)
Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@53ba89] after transaction
  [DEBUG] 2018-05-21 17:32:08,861 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource


  ---------scoreService updateScore end---------
[DEBUG] 2018-05-21 17:32:08,868 method:org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:132)
Resolving exception from handler [public void com.paic.ssm.login.controller.LoginController.insert()]: java.lang.ArithmeticException: / by zero
  [DEBUG] 2018-05-21 17:32:08,869 method:org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:132)
Resolving exception from handler [public void com.paic.ssm.login.controller.LoginController.insert()]: java.lang.ArithmeticException: / by zero
  [DEBUG] 2018-05-21 17:32:08,869 method:org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:132)


Resolving exception from handler [public void com.paic.ssm.login.controller.LoginController.insert()]: java.lang.ArithmeticException: / by zero
  [DEBUG] 2018-05-21 17:32:08,869 method:org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
Could not complete request
  java.lang.ArithmeticException: / by zero
	at com.paic.ssm.user.service.impl.UserServiceImpl.update(UserServiceImpl.java:49)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

能夠看到,在userService#update方法裏面執行時,直到scoreService執行完畢,事務就緊接着提交,最後時刻,方法再拋出異常,已經爲時已晚,查驗DB,數據已修改,說明調用方userservice的異常不會影響被調用方scoreservice的事務

能夠繼續驗證,打開scoreService的updateScore方法的異常測試代碼,會發現scoreService已正常回滾,而userService沒法回滾,很符合常識

---------scoreService updateScore start---------
[DEBUG] 2018-05-21 17:38:15,272 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'transactionManager'
  [DEBUG] 2018-05-21 17:38:15,276 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:366)
Creating new transaction with name [com.paic.ssm.user.service.impl.ScoreServiceImpl.updateScore]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
  [DEBUG] 2018-05-21 17:38:15,278 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:204)
Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@a6168] for JDBC transaction


Initiating transaction rollback
  [DEBUG] 2018-05-21 17:38:15,289 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doRollback(DataSourceTransactionManager.java:282)
Rolling back JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@a6168]
  17:38:15.319 [http-bio-8080-exec-3] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization rolling back SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@bf617d]

4:UserServiceImpl#update不開啓事務,ScoreService#updateScore不開啓事務,不輸出日誌了

此時,二者均不會被事務做用,異常不會回滾,不贅述。

 

因爲事務傳播方式較多,可是系統大部分都是使用默認的事務傳播方式,故其餘的模式就再也不測試了,通過測試發現,結果符合spring的傳播方式的定義。

總結一下,若是要使用事務,通常是在service方法上進行標註,若初始調用某service,則事務會開啓,即使後續調用其餘service方法,事務仍會繼續傳播,全部service共用一個事務,任一方法拋出異常,若是被初始調用的service捕獲,則方法回滾。若異常被底層預先捕獲,則沒法回滾了。

特別注意的是,若是初始調用無事務,則後續的方法調用不會影響它,即事務只會日後傳播。。。

 

雖然spring事務傳播的定義已經表述很清楚了,可是經過小實驗,又加深了印象。

下期討論下service裏面開啓新線程,事務的傳播。。。

相關文章
相關標籤/搜索