- 控制层已忽略,通过调用service层的 test_thread2进行该试验,该方法先添加一个name值为
add1
的用户,接着启用新线程,该线程先添加一个name值为add6
的用户,接着删除一个id为6
的用户,测试事务在多线程中的行为 - 第一次测试
@Transactional
public User add(User u) {
userDao.insert(u);
return u;
}
@Transactional
public int delete(int id) {
int i = userDao.deleteByPrimaryKey(id);
return i;
}
@Transactional(rollbackFor = Exception.class)
public void test_thread2() throws ExecutionException, InterruptedException {
User user = new User();
user.setUsername("add1");
user.setPassword("addPwd1");
add(user);//同一类中的添加方法
// add和delete未标注事务
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
User user = new User();
user.setUsername("add6");
user.setPassword("addPwd6");
add(user);
delete(6);//删除,同一类中的删除方法
throw new Exception("模拟异常操作");//抛异常
}
};
ExecutorService service = Executors.newCachedThreadPool();
Future<String> submit = service.submit(callable);
service.shutdown();
System.out.println(submit.get());
}
结果是外层事务回滚,就是线程外添加失败,但是线程内的添加和删除成功,参考Java Spring事务管理与@Transactional注解式事务详解得知类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰
- 第二次测试,将add,delete 移动到新的service上(TestService),启用事务注解
@Transactional
public User add(User u) {
userDao.insert(u);
return u;
}
@Transactional(rollbackFor = Exception.class)
public void test_thread2() throws ExecutionException, InterruptedException {
User user = new User();
user.setUsername("add1");
user.setPassword("addPwd1");
add(user);
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
User user = new User();
user.setUsername("add6");
user.setPassword("addPwd6");
testService.add(user);
testService.delete(6);
throw new Exception("模拟线程内异常");
}
};
ExecutorService service = Executors.newCachedThreadPool();
Future<String> submit = service.submit(callable);
service.shutdown();
System.out.println(submit.get());
}
@Service
@Transactional(rollbackFor = Exception.class)
public class TestService {
@Autowired
UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
public void add(User user) {
userMapper.insert(user);
}
@Transactional(rollbackFor = Exception.class)
public void delete(int user) {
userMapper.deleteByPrimaryKey(user);
}
}
[DEBUG-console] 2019/02/04,12:04:57.007|GET "/user/test_thread2", parameters={}
[DEBUG-console] 2019/02/04,12:04:57.019|Mapped to public java.lang.String com.mico.emptyspring.controller.UserProcessController.transaction_thread2() throws java.util.concurrent.ExecutionException,java.lang.InterruptedException
[DEBUG-console] 2019/02/04,12:04:57.099|Creating new transaction with name [com.mico.emptyspring.service.UserProcessServiceImpl.test_thread2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
[DEBUG-console] 2019/02/04,12:04:57.100|Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test?useSSL=true]
[DEBUG-console] 2019/02/04,12:04:57.770|Acquired Connection [com.mysql.jdbc.JDBC4Connection@5b96f826] for JDBC transaction
[DEBUG-console] 2019/02/04,12:04:57.784|Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@5b96f826] to manual commit
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3771b8c9]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@5b96f826] will be managed by Spring
==> Preparing: insert into user (id, username, password) values (?, ?, ?)
==> Parameters: null, add1(String), addPwd1(String)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3771b8c9]
[DEBUG-console] 2019/02/04,12:04:58.087|Creating new transaction with name [com.mico.emptyspring.service.TestService.add]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
[DEBUG-console] 2019/02/04,12:04:58.087|Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test?useSSL=true]
[DEBUG-console] 2019/02/04,12:04:58.096|Acquired Connection [com.mysql.jdbc.JDBC4Connection@1e43460c] for JDBC transaction
[DEBUG-console] 2019/02/04,12:04:58.096|Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@1e43460c] to manual commit
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3b6e24ef]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@1e43460c] will be managed by Spring
==> Preparing: insert into user (id, username, password) values (?, ?, ?)
==> Parameters: null, add6(String), addPwd6(String)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3b6e24ef]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3b6e24ef]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3b6e24ef]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3b6e24ef]
[DEBUG-console] 2019/02/04,12:04:58.113|Initiating transaction commit
[DEBUG-console] 2019/02/04,12:04:58.113|Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@1e43460c]
[DEBUG-console] 2019/02/04,12:04:58.114|Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1e43460c] after transaction
[DEBUG-console] 2019/02/04,12:04:58.114|Returning JDBC Connection to DataSource
[DEBUG-console] 2019/02/04,12:04:58.122|Creating new transaction with name [com.mico.emptyspring.service.TestService.delete]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
[DEBUG-console] 2019/02/04,12:04:58.122|Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test?useSSL=true]
[DEBUG-console] 2019/02/04,12:04:58.134|Acquired Connection [com.mysql.jdbc.JDBC4Connection@c28bef4] for JDBC transaction
[DEBUG-console] 2019/02/04,12:04:58.134|Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@c28bef4] to manual commit
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@35e6b79b]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@c28bef4] will be managed by Spring
==> Preparing: delete from user where id = ?
==> Parameters: 6(Integer)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@35e6b79b]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@35e6b79b]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@35e6b79b]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@35e6b79b]
[DEBUG-console] 2019/02/04,12:04:58.140|Initiating transaction commit
[DEBUG-console] 2019/02/04,12:04:58.140|Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@c28bef4]
[DEBUG-console] 2019/02/04,12:04:58.141|Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@c28bef4] after transaction
[DEBUG-console] 2019/02/04,12:04:58.141|Returning JDBC Connection to DataSource
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3771b8c9]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3771b8c9]
[DEBUG-console] 2019/02/04,12:04:58.148|Initiating transaction rollback
[DEBUG-console] 2019/02/04,12:04:58.149|Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@5b96f826]
[DEBUG-console] 2019/02/04,12:04:58.151|Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5b96f826] after transaction
[DEBUG-console] 2019/02/04,12:04:58.151|Returning JDBC Connection to DataSource
[ERROR-console] 2019/02/04,12:04:58.492|{"consumed":"1127ms","params":{},"result":null,"url":"/user/test_thread2","ip":"0:0:0:0:0:0:0:1","throwable":"java.util.concurrent.ExecutionException: java.lang.Exception: 模拟线程内异常"}
java.util.concurrent.ExecutionException: java.lang.Exception: 模拟线程内异常
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.mico.emptyspring.service.UserProcessServiceImpl.test_thread2(UserProcessServiceImpl.java:188)
at com.mico.emptyspring.service.UserProcessServiceImpl$$FastClassBySpringCGLIB$$9abee637.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.mico.emptyspring.service.UserProcessServiceImpl$$EnhancerBySpringCGLIB$$406a6b4d.test_thread2(<generated>)
at com.mico.emptyspring.controller.UserProcessController.transaction_thread2(UserProcessController.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:94)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:502)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1156)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:684)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1539)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1495)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.Exception: 模拟线程内异常
at com.mico.emptyspring.service.UserProcessServiceImpl$2.call(UserProcessServiceImpl.java:182)
at com.mico.emptyspring.service.UserProcessServiceImpl$2.call(UserProcessServiceImpl.java:174)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
... 1 more
[DEBUG-console] 2019/02/04,12:04:58.537|Failed to complete request: java.util.concurrent.ExecutionException: java.lang.Exception: 模拟线程内异常
外层添加失败,内部添加和删除都成功,但是我又观察了一下上边的日志,进行抽取:
- com.mysql.jdbc.JDBC4Connection@5b96f826 insert name=add1
- 线程内com.mysql.jdbc.JDBC4Connection@1e43460c insert name=add6
...Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1e43460c] after transaction - 线程内com.mysql.jdbc.JDBC4Connection@c28bef4 delete id=6
...Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@c28bef4] after transaction - Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@5b96f826]
...Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5b96f826] after transaction - 也就是说,其实启用了三个事务,一个线程外添加,一个线程内添加,一个线程内删除,但是执行完线程内添加和线程内删除后线程内抛错了,被主线程catch到了,导致线程外事务被回滚,线程内的单独俩个添加和删除都开启了事务并且事务已经被提交,不被影响,就是说线程内调用service的带有@Transactional注解的方法是有事务的,这么说来,是代码有问题,必须要让线程内的添加和删除处在同一个事务下
- add,delete 移动到新的service上(TestService),启用事务注解,新增方法addDeleteOneTransaction,启用事务注解
@Service
@Transactional(rollbackFor = Exception.class)
public class TestService {
@Autowired
UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
public void add(User user) {
userMapper.insert(user);
}
@Transactional(rollbackFor = Exception.class)
public void delete(int user) {
userMapper.deleteByPrimaryKey(user);
}
@Transactional(rollbackFor = Exception.class)
public void addDeleteOneTransaction(User user,int id) {
add(user);
delete(id);
}
}
@Transactional(rollbackFor = Exception.class)
public void test_thread2() throws ExecutionException, InterruptedException {
User user = new User();
user.setUsername("add1");
user.setPassword("addPwd1");
add(user);
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
User user = new User();
user.setUsername("add6");
user.setPassword("addPwd6");
testService.addDeleteOneTransaction(user,6);
throw new Exception("模拟线程内异常");
}
};
ExecutorService service = Executors.newCachedThreadPool();
Future<String> submit = service.submit(callable);
service.shutdown();
System.out.println(submit.get());
}
[DEBUG-console] 2019/02/04,13:20:14.289|GET "/user/test_thread2", parameters={}
[DEBUG-console] 2019/02/04,13:20:14.313|Mapped to public java.lang.String com.mico.emptyspring.controller.UserProcessController.transaction_thread2() throws java.util.concurrent.ExecutionException,java.lang.InterruptedException
[DEBUG-console] 2019/02/04,13:20:14.382|Creating new transaction with name [com.mico.emptyspring.service.UserProcessServiceImpl.test_thread2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
[DEBUG-console] 2019/02/04,13:20:14.383|Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test?useSSL=true]
[DEBUG-console] 2019/02/04,13:20:14.798|Acquired Connection [com.mysql.jdbc.JDBC4Connection@3a21dee] for JDBC transaction
[DEBUG-console] 2019/02/04,13:20:14.819|Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@3a21dee] to manual commit
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27974ebf]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@3a21dee] will be managed by Spring
==> Preparing: insert into user (id, username, password) values (?, ?, ?)
==> Parameters: null, add1(String), addPwd1(String)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27974ebf]
[DEBUG-console] 2019/02/04,13:20:14.979|Creating new transaction with name [com.mico.emptyspring.service.TestService.addDeleteOneTransaction]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
[DEBUG-console] 2019/02/04,13:20:14.980|Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test?useSSL=true]
[DEBUG-console] 2019/02/04,13:20:14.988|Acquired Connection [com.mysql.jdbc.JDBC4Connection@2a8d7dcd] for JDBC transaction
[DEBUG-console] 2019/02/04,13:20:14.988|Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@2a8d7dcd] to manual commit
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@fa009b7]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@2a8d7dcd] will be managed by Spring
==> Preparing: insert into user (id, username, password) values (?, ?, ?)
==> Parameters: null, add6(String), addPwd6(String)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@fa009b7]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@fa009b7] from current transaction
==> Preparing: delete from user where id = ?
==> Parameters: 6(Integer)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@fa009b7]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@fa009b7]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@fa009b7]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@fa009b7]
[DEBUG-console] 2019/02/04,13:20:15.017|Initiating transaction commit
[DEBUG-console] 2019/02/04,13:20:15.017|Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@2a8d7dcd]
[DEBUG-console] 2019/02/04,13:20:15.019|Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@2a8d7dcd] after transaction
[DEBUG-console] 2019/02/04,13:20:15.020|Returning JDBC Connection to DataSource
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27974ebf]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27974ebf]
[DEBUG-console] 2019/02/04,13:20:15.074|Initiating transaction rollback
[DEBUG-console] 2019/02/04,13:20:15.075|Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@3a21dee]
[DEBUG-console] 2019/02/04,13:20:15.108|Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3a21dee] after transaction
[DEBUG-console] 2019/02/04,13:20:15.109|Returning JDBC Connection to DataSource
[ERROR-console] 2019/02/04,13:20:15.307|{"consumed":"816ms","params":{},"result":null,"url":"/user/test_thread2","ip":"0:0:0:0:0:0:0:1","throwable":"java.util.concurrent.ExecutionException: java.lang.Exception: 模拟线程内异常"}
java.util.concurrent.ExecutionException: java.lang.Exception: 模拟线程内异常
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.mico.emptyspring.service.UserProcessServiceImpl.test_thread2(UserProcessServiceImpl.java:180)
at com.mico.emptyspring.service.UserProcessServiceImpl$$FastClassBySpringCGLIB$$9abee637.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.mico.emptyspring.service.UserProcessServiceImpl$$EnhancerBySpringCGLIB$$3f534ae.test_thread2(<generated>)
at com.mico.emptyspring.controller.UserProcessController.transaction_thread2(UserProcessController.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:94)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:502)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1156)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:684)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1539)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1495)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.Exception: 模拟线程内异常
at com.mico.emptyspring.service.UserProcessServiceImpl$2.call(UserProcessServiceImpl.java:174)
at com.mico.emptyspring.service.UserProcessServiceImpl$2.call(UserProcessServiceImpl.java:167)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
... 1 more
[DEBUG-console] 2019/02/04,13:20:15.342|Failed to complete request: java.util.concurrent.ExecutionException: java.lang.Exception: 模拟线程内异常
外层添加失败[回滚],内部添加和删除都成功
-
抽取日志信息:
- JDBC Connection [com.mysql.jdbc.JDBC4Connection@3a21dee] insert name=add1
- 线程内JDBC Connection [com.mysql.jdbc.JDBC4Connection@2a8d7dcd] insert name=add6
- 线程内JDBC Connection [com.mysql.jdbc.JDBC4Connection@2a8d7dcd] delete id=6
...Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@2a8d7dcd] after transaction - Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@3a21dee]
...Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3a21dee] after transaction - 现在只有2个jdbc连接,2个事务,线程内一个事务,线程外一个事务,但是线程内的事务未出现异常,线程内事务已提交,线程内出现异常,被线程外catch,线程外事务被回滚
-
我寻思addDeleteOneTransaction,这时候新启线程起的事务,内部未抛异常,所以是不会回滚的,在addDeleteOneTransaction内部抛异常试试
@Service
@Transactional(rollbackFor = Exception.class)
public class TestService {
@Autowired
UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
public void add(User user) {
userMapper.insert(user);
}
@Transactional(rollbackFor = Exception.class)
public void delete(int user) {
userMapper.deleteByPrimaryKey(user);
}
@Transactional(rollbackFor = Exception.class)
public void addDeleteOneTransaction(User user,int id) throws Exception{
add(user);
delete(id);
throw new Exception("模拟事务内异常");
}
}
[DEBUG-console] 2019/02/04,13:37:12.981|GET "/user/test_thread2", parameters={}
[DEBUG-console] 2019/02/04,13:37:12.991|Mapped to public java.lang.String com.mico.emptyspring.controller.UserProcessController.transaction_thread2() throws java.util.concurrent.ExecutionException,java.lang.InterruptedException
[DEBUG-console] 2019/02/04,13:37:13.037|Creating new transaction with name [com.mico.emptyspring.service.UserProcessServiceImpl.test_thread2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
[DEBUG-console] 2019/02/04,13:37:13.038|Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test?useSSL=true]
[DEBUG-console] 2019/02/04,13:37:13.465|Acquired Connection [com.mysql.jdbc.JDBC4Connection@22bb0391] for JDBC transaction
[DEBUG-console] 2019/02/04,13:37:13.473|Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@22bb0391] to manual commit
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@181033db]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@22bb0391] will be managed by Spring
==> Preparing: insert into user (id, username, password) values (?, ?, ?)
==> Parameters: null, add1(String), addPwd1(String)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@181033db]
[DEBUG-console] 2019/02/04,13:37:13.632|Creating new transaction with name [com.mico.emptyspring.service.TestService.addDeleteOneTransaction]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
[DEBUG-console] 2019/02/04,13:37:13.633|Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test?useSSL=true]
[DEBUG-console] 2019/02/04,13:37:13.642|Acquired Connection [com.mysql.jdbc.JDBC4Connection@6f526621] for JDBC transaction
[DEBUG-console] 2019/02/04,13:37:13.642|Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@6f526621] to manual commit
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a7f0ae0]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@6f526621] will be managed by Spring
==> Preparing: insert into user (id, username, password) values (?, ?, ?)
==> Parameters: null, add6(String), addPwd6(String)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a7f0ae0]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a7f0ae0] from current transaction
==> Preparing: delete from user where id = ?
==> Parameters: 6(Integer)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a7f0ae0]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a7f0ae0]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a7f0ae0]
[DEBUG-console] 2019/02/04,13:37:13.662|Initiating transaction rollback
[DEBUG-console] 2019/02/04,13:37:13.662|Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@6f526621]
[DEBUG-console] 2019/02/04,13:37:13.683|Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6f526621] after transaction
[DEBUG-console] 2019/02/04,13:37:13.684|Returning JDBC Connection to DataSource
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@181033db]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@181033db]
[DEBUG-console] 2019/02/04,13:37:13.712|Initiating transaction rollback
[DEBUG-console] 2019/02/04,13:37:13.712|Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@22bb0391]
[DEBUG-console] 2019/02/04,13:37:13.715|Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@22bb0391] after transaction
[DEBUG-console] 2019/02/04,13:37:13.715|Returning JDBC Connection to DataSource
[ERROR-console] 2019/02/04,13:37:14.173|{"consumed":"927ms","params":{},"result":null,"url":"/user/test_thread2","ip":"0:0:0:0:0:0:0:1","throwable":"java.util.concurrent.ExecutionException: java.lang.Exception: 模拟事务内异常"}
java.util.concurrent.ExecutionException: java.lang.Exception: 模拟事务内异常
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.mico.emptyspring.service.UserProcessServiceImpl.test_thread2(UserProcessServiceImpl.java:180)
at com.mico.emptyspring.service.UserProcessServiceImpl$$FastClassBySpringCGLIB$$9abee637.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.mico.emptyspring.service.UserProcessServiceImpl$$EnhancerBySpringCGLIB$$27719087.test_thread2(<generated>)
at com.mico.emptyspring.controller.UserProcessController.transaction_thread2(UserProcessController.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:94)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:502)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1156)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:684)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1539)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1495)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.Exception: 模拟事务内异常
at com.mico.emptyspring.service.TestService.addDeleteOneTransaction(TestService.java:30)
at com.mico.emptyspring.service.TestService$$FastClassBySpringCGLIB$$137ec6fd.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.mico.emptyspring.service.TestService$$EnhancerBySpringCGLIB$$c2e8de99.addDeleteOneTransaction(<generated>)
at com.mico.emptyspring.service.UserProcessServiceImpl$2.call(UserProcessServiceImpl.java:173)
at com.mico.emptyspring.service.UserProcessServiceImpl$2.call(UserProcessServiceImpl.java:167)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
... 1 more
[DEBUG-console] 2019/02/04,13:37:14.208|Failed to complete request: java.util.concurrent.ExecutionException: java.lang.Exception: 模拟事务内异常
内层事务回滚,外层事务也回滚了
-
抽取日志:
- 线程外JDBC Connection [com.mysql.jdbc.JDBC4Connection@22bb0391] insert name=add1
- 线程内JDBC Connection [com.mysql.jdbc.JDBC4Connection@6f526621] insert name=add6
- 线程内JDBC Connection [com.mysql.jdbc.JDBC4Connection@6f526621] delete id=6
- Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@6f526621]
...Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6f526621] after transaction - Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@22bb0391]
...Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@22bb0391] after transaction
-
最后我把TestService删了,把addDeleteOneTransaction方法移动到和test_thread2服务方法同一个类中,验证只有来自外部的方法调用才会被AOP代理捕获[要外部调用service才会有事务],毕竟第一次测试没保存日志
@Transactional
public User add(User u) {
userDao.insert(u);
return u;
}
@Transactional
public int delete(int id) {
int i = userDao.deleteByPrimaryKey(id);
return i;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addDeleteOneTransaction(User user,int id) throws Exception{
add(user);
delete(id);
throw new Exception("模拟事务内异常");
}
@Override
@Transactional(rollbackFor = Exception.class)
public void test_thread2() throws ExecutionException, InterruptedException {
//线程外执行一次添加
//线程内执行一次添加,一次删除,抛出抛异常
//2次添加和删除都失败
User user = new User();
user.setUsername("add1");
user.setPassword("addPwd1");
add(user);
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
User user = new User();
user.setUsername("add6");
user.setPassword("addPwd6");
addDeleteOneTransaction(user,6);
throw new Exception("模拟线程内异常");
}
};
ExecutorService service = Executors.newCachedThreadPool();
Future<String> submit = service.submit(callable);
service.shutdown();
System.out.println(submit.get());
}
[DEBUG-console] 2019/02/05,09:47:47.950|GET "/user/test_thread2", parameters={}
[DEBUG-console] 2019/02/05,09:47:47.967|Mapped to public java.lang.String com.mico.emptyspring.controller.UserProcessController.transaction_thread2() throws java.util.concurrent.ExecutionException,java.lang.InterruptedException
[DEBUG-console] 2019/02/05,09:47:48.050|Creating new transaction with name [com.mico.emptyspring.service.UserProcessServiceImpl.test_thread2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
[DEBUG-console] 2019/02/05,09:47:48.051|Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test?useSSL=true]
[DEBUG-console] 2019/02/05,09:47:48.473|Acquired Connection [com.mysql.jdbc.JDBC4Connection@3e24ee40] for JDBC transaction
[DEBUG-console] 2019/02/05,09:47:48.481|Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@3e24ee40] to manual commit
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7c2b57c5]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@3e24ee40] will be managed by Spring
==> Preparing: insert into user (id, username, password) values (?, ?, ?)
==> Parameters: null, add1(String), addPwd1(String)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7c2b57c5]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27c61dd7] was not registered for synchronization because synchronization is not active
[DEBUG-console] 2019/02/05,09:47:48.613|Fetching JDBC Connection from DataSource
[DEBUG-console] 2019/02/05,09:47:48.613|Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test?useSSL=true]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@683cfd64] will not be managed by Spring
==> Preparing: insert into user (id, username, password) values (?, ?, ?)
==> Parameters: null, add6(String), addPwd6(String)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27c61dd7]
[DEBUG-console] 2019/02/05,09:47:48.628|Returning JDBC Connection to DataSource
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@427e822] was not registered for synchronization because synchronization is not active
[DEBUG-console] 2019/02/05,09:47:48.645|Fetching JDBC Connection from DataSource
[DEBUG-console] 2019/02/05,09:47:48.646|Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test?useSSL=true]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@65deaa9] will not be managed by Spring
==> Preparing: delete from user where id = ?
==> Parameters: 6(Integer)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@427e822]
[DEBUG-console] 2019/02/05,09:47:48.661|Returning JDBC Connection to DataSource
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7c2b57c5]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7c2b57c5]
[DEBUG-console] 2019/02/05,09:47:48.662|Initiating transaction rollback
[DEBUG-console] 2019/02/05,09:47:48.663|Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@3e24ee40]
[DEBUG-console] 2019/02/05,09:47:48.664|Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3e24ee40] after transaction
[DEBUG-console] 2019/02/05,09:47:48.664|Returning JDBC Connection to DataSource
[ERROR-console] 2019/02/05,09:47:48.848|{"consumed":"700ms","params":{},"result":null,"url":"/user/test_thread2","ip":"0:0:0:0:0:0:0:1","throwable":"java.util.concurrent.ExecutionException: java.lang.Exception: 模拟事务内异常"}
java.util.concurrent.ExecutionException: java.lang.Exception: 模拟事务内异常
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.mico.emptyspring.service.UserProcessServiceImpl.test_thread2(UserProcessServiceImpl.java:188)
at com.mico.emptyspring.service.UserProcessServiceImpl$$FastClassBySpringCGLIB$$9abee637.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.mico.emptyspring.service.UserProcessServiceImpl$$EnhancerBySpringCGLIB$$e5eb887c.test_thread2(<generated>)
at com.mico.emptyspring.controller.UserProcessController.transaction_thread2(UserProcessController.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:94)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:502)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1156)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:684)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1539)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1495)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.Exception: 模拟事务内异常
at com.mico.emptyspring.service.UserProcessServiceImpl.addDeleteOneTransaction(UserProcessServiceImpl.java:161)
at com.mico.emptyspring.service.UserProcessServiceImpl$2.call(UserProcessServiceImpl.java:181)
at com.mico.emptyspring.service.UserProcessServiceImpl$2.call(UserProcessServiceImpl.java:175)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
... 1 more
[DEBUG-console] 2019/02/05,09:47:48.893|Failed to complete request: java.util.concurrent.ExecutionException: java.lang.Exception: 模拟事务内异常
-
日志抽取
- 线程外JDBC Connection [com.mysql.jdbc.JDBC4Connection@3e24ee40] for JDBC transaction insert name=add1
- 线程内JDBC Connection [com.mysql.jdbc.JDBC4Connection@683cfd64] will not be managed by Spring [无事务] insert name=add6
- 线程内JDBC Connection [com.mysql.jdbc.JDBC4Connection@65deaa9] will not be managed by Spring[无事务] delete id = 6
- Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@3e24ee40]
...Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3e24ee40] after transaction - 可以看到线程内的添加和删除是没有事务的(will not be managed by Spring),线程内异常导致线程外事务回滚,的确,类内部方法调用本类内部的其他方法并不会引起事务行为
-
结论:
- 默认情况下,只有来自外部的方法调用才会被AOP代理捕获[要外部调用service才会有事务],也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰
- 如果在一个被@Transactional修饰的服务方法内启用多线程,那么该服务方法的事务与线程内的事务是2个完全不相关的事务,也就是说在@Transactional注解的服务方法会产生一个新的线程的情况下,事务是不会从调用者线程传播到新建线程的。
网友评论