第一节讲述了不同service之间事务的传播,经过小实验,还原了spring的事务传播结论,这期实验下service中开启新线程后,事务如何传播
首先是controller发起请求:
@RequestMapping(value="/test2")
public void insert2() {
System.out.println("---------spring mvc update---------");
userService.update2(111,"aaaazz");
System.out.println("---------spring mvc all---------");
}
userServiceImpl
@Override
@Transactional
public void update2(int id,String username) {
System.out.println("---------UserServiceImpl updateAge start---------");
updateAge(id);
System.out.println("---------UserServiceImpl updateAge end---------");
MyThread thread = new MyThread(scoreService);
thread.start();
// int i = 1 / 0;
System.out.println("---------UserServiceImpl method over---------");
}
涉及的内部类:
class MyThread extends Thread{
IScoreService scoreService;
public MyThread (IScoreService scoreService){
this.scoreService = scoreService;
}
@Override
public void run() {
System.out.println("---------scoreService updateScore start---------");
this.scoreService.updateScore(1);
System.out.println("---------scoreService updateScore end---------");
}
}
scoreServiceImpl
@Override
@Transactional
public void updateScore(int id) {
scoreDao.updateScore(id);
// try{
int i = 1 / 0; //exception测试注释
i = 1;
// } catch (Exception e){
// e.printStackTrace();
// }
}
1:userServiceImpl和scoreServiceImpl都打开事务
日志输出:
---------spring mvc update---------
[DEBUG] 2018-05-23 20:44:38,367 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-23 20:44:38,370 method:org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource.getTransactionAttribute(AbstractFallbackTransactionAttributeSource.java:107)
Adding transactional method 'UserServiceImpl.update2' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
[DEBUG] 2018-05-23 20:44:38,374 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'transactionManager'
[DEBUG] 2018-05-23 20:44:38,383 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:366)
Creating new transaction with name [com.paic.ssm.user.service.impl.UserServiceImpl.update2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
[DEBUG] 2018-05-23 20:44:38,384 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:204)
Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@139b811] for JDBC transaction
[DEBUG] 2018-05-23 20:44:38,391 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:221)
Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@139b811] to manual commit
---------UserServiceImpl updateAge start---------
20:44:38.401 [http-bio-8080-exec-9] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
20:44:38.408 [http-bio-8080-exec-9] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@195fbac]
20:44:38.458 [http-bio-8080-exec-9] DEBUG o.m.s.t.SpringManagedTransaction - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@139b811] will be managed by Spring
20:44:38.461 [http-bio-8080-exec-9] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@139b811]
20:44:38.467 [http-bio-8080-exec-9] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ==> Preparing: update user_t set age = ?
20:44:38.644 [http-bio-8080-exec-9] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ==> Parameters: 111(Integer)
[DEBUG] 2018-05-23 20:44:38,650 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10010, pstmt-20000} enter cache
20:44:38.650 [http-bio-8080-exec-9] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@195fbac]
---------UserServiceImpl updateAge end---------
---------UserServiceImpl method over---------
---------scoreService updateScore start---------
[DEBUG] 2018-05-23 20:44:38,652 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:753)
Initiating transaction commit
[DEBUG] 2018-05-23 20:44:38,653 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'transactionManager'
[DEBUG] 2018-05-23 20:44:38,653 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DataSourceTransactionManager.java:267)
Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@139b811]
[DEBUG] 2018-05-23 20:44:38,653 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-23 20:44:38,654 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:204)
Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1876493] for JDBC transaction
[DEBUG] 2018-05-23 20:44:38,654 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:221)
Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1876493] to manual commit
20:44:38.655 [Thread-2] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
20:44:38.655 [Thread-2] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1b63214]
20:44:38.655 [Thread-2] DEBUG o.m.s.t.SpringManagedTransaction - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1876493] will be managed by Spring
20:44:38.655 [Thread-2] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1876493]
20:44:38.655 [Thread-2] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==> Preparing: update score_t set score = ?
20:44:38.655 [Thread-2] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==> Parameters: 1(Integer)
[DEBUG] 2018-05-23 20:44:38,656 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10009, pstmt-20001} enter cache
20:44:38.656 [Thread-2] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1b63214]
[DEBUG] 2018-05-23 20:44:38,656 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:753)
Initiating transaction commit
[DEBUG] 2018-05-23 20:44:38,656 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DataSourceTransactionManager.java:267)
Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1876493]
20:44:38.732 [Thread-2] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1b63214]
20:44:38.732 [Thread-2] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1b63214]
[DEBUG] 2018-05-23 20:44:38,734 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager.java:325)
Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1876493] after transaction
[DEBUG] 2018-05-23 20:44:38,734 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
---------scoreService updateScore end---------
20:44:38.819 [http-bio-8080-exec-9] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@195fbac]
20:44:38.819 [http-bio-8080-exec-9] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@195fbac]
[DEBUG] 2018-05-23 20:44:38,820 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager.java:325)
Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@139b811] after transaction
[DEBUG] 2018-05-23 20:44:38,821 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
---------spring mvc all---------
可以看到,两个方法均新建了新的事务,说明两者并不是共享同一事务
2:userServiceImpl打开事务,scoreServiceImpl关闭事务
经过试验,发现userServiceImpl开启了新的事务,而scoreServiceImpl并未开启事务。。。
继续测试回滚
仅打开scoreServiceImpl的异常测试代码,并且不捕获异常,输出如下:
eb.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:921)
Last-Modified value for [/tx-spread/login/test2.do] is: -1
---------spring mvc update---------
[DEBUG] 2018-05-23 20:58:39,560 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-23 20:58:39,566 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'transactionManager'
[DEBUG] 2018-05-23 20:58:39,572 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:366)
Creating new transaction with name [com.paic.ssm.user.service.impl.UserServiceImpl.update2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
[DEBUG] 2018-05-23 20:58:39,573 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:204)
Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@150d517] for JDBC transaction
[DEBUG] 2018-05-23 20:58:39,578 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:221)
Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@150d517] to manual commit
---------UserServiceImpl updateAge start---------
20:58:39.584 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
20:58:39.589 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@15f4f4d]
20:58:39.633 [http-bio-8080-exec-6] DEBUG o.m.s.t.SpringManagedTransaction - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@150d517] will be managed by Spring
20:58:39.635 [http-bio-8080-exec-6] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@150d517]
20:58:39.639 [http-bio-8080-exec-6] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ==> Preparing: update user_t set age = ?
20:58:39.774 [http-bio-8080-exec-6] DEBUG c.p.ssm.user.dao.IUserDao.updateAge - ==> Parameters: 111(Integer)
[DEBUG] 2018-05-23 20:58:39,778 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10010, pstmt-20000} enter cache
20:58:39.779 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@15f4f4d]
---------UserServiceImpl updateAge end---------
---------UserServiceImpl method over---------
---------scoreService updateScore start---------
[DEBUG] 2018-05-23 20:58:39,781 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:753)
Initiating transaction commit
[DEBUG] 2018-05-23 20:58:39,782 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DataSourceTransactionManager.java:267)
Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@150d517]
[DEBUG] 2018-05-23 20:58:39,782 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:243)
Returning cached instance of singleton bean 'transactionManager'
[DEBUG] 2018-05-23 20:58:39,784 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-23 20:58:39,784 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:204)
Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@190f65c] for JDBC transaction
[DEBUG] 2018-05-23 20:58:39,784 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:221)
Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@190f65c] to manual commit
20:58:39.785 [Thread-2] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
20:58:39.785 [Thread-2] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@cc6826]
20:58:39.785 [Thread-2] DEBUG o.m.s.t.SpringManagedTransaction - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@190f65c] will be managed by Spring
20:58:39.785 [Thread-2] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@190f65c]
20:58:39.785 [Thread-2] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==> Preparing: update score_t set score = ?
20:58:39.786 [Thread-2] DEBUG c.p.s.user.dao.IScoreDao.updateScore - ==> Parameters: 1(Integer)
[DEBUG] 2018-05-23 20:58:39,787 method:com.alibaba.druid.pool.PreparedStatementPool.put(PreparedStatementPool.java:123)
{conn-10009, pstmt-20001} enter cache
20:58:39.788 [Thread-2] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@cc6826]
[DEBUG] 2018-05-23 20:58:39,788 method:org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:844)
Initiating transaction rollback
[DEBUG] 2018-05-23 20:58:39,788 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doRollback(DataSourceTransactionManager.java:282)
Rolling back JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@190f65c]
20:58:39.839 [Thread-2] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization rolling back SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@cc6826]
20:58:39.839 [Thread-2] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@cc6826]
[DEBUG] 2018-05-23 20:58:39,840 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager.java:325)
Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@190f65c] after transaction
[DEBUG] 2018-05-23 20:58:39,840 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
Exception in thread "Thread-2" java.lang.ArithmeticException: / by zero
at com.paic.ssm.user.service.impl.ScoreServiceImpl.updateScore(ScoreServiceImpl.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at com.sun.proxy.$Proxy17.updateScore(Unknown Source)
at com.paic.ssm.user.service.impl.MyThread.run(UserServiceImpl.java:103)
20:58:39.933 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@15f4f4d]
20:58:39.933 [http-bio-8080-exec-6] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@15f4f4d]
[DEBUG] 2018-05-23 20:58:39,934 method:org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager.java:325)
Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@150d517] after transaction
[DEBUG] 2018-05-23 20:58:39,935 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
---------spring mvc all---------
userServiceImpl事务提交了,scoreServiceImpl回滚了(日志抛出挺长的),和DB结果一致,由于两个方法位于不同的事务中,也很符合预期
当scoreServiceImpl关闭事务,发现userServiceImpl事务回滚,而是scoreServiceImpl并未回滚,也符合预期
由于两个service位于不同的事务,所以提交和回滚均是按照自身的事务规则。
从实验可以看出,如果在事务处理的方法中开启了子线程,事务是不会传播的,子线程的事务由其本身调用的方法的事务决定的,如果子线程调用的方法无事务,那么其就以无事务方式运行