赞
踩
目录
线上的项目出现,Spring的事务回滚了,但仍触发了事务提交成功的事件。这就导致原先通过异步事件解藕的业务处理数据会不正确。例如业务更新成功后才发送通知消息的处理,会出现业务回滚了,仍触发了发送消息通知的处理。
Spring Boot 2.0.0
+ druid 1.1.9
事务的传播级别: PROPAGATION_NESTED
- @Configuration
- public class TxAnoConfig {
-
-
- private static final int TX_METHOD_TIMEOUT = 3600;
- private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.abc.service..*.*(..)) ";
- /*事务拦截类型*/
- @Bean("txSource")
- public TransactionAttributeSource transactionAttributeSource(){
- NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
-
- /**
- * REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- * SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
- * MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
- * REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
- * NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- * NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
- * NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
- */
- readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS );
- /*当前存在事务就使用当前事务,当前不存在事务就创建一个新的事务*/
- //RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
- //requiredTx.setRollbackRules(
- // Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
- //requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
- RuleBasedTransactionAttribute nestedTx = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_NESTED,
- Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
- nestedTx.setTimeout(TX_METHOD_TIMEOUT);
- Map<String, TransactionAttribute> txMap = new HashMap<>();
- //定义哪些方法启动事务
- txMap.put("add*", nestedTx);
- txMap.put("save*", nestedTx);
- txMap.put("insert*", nestedTx);
- txMap.put("update*", nestedTx);
- txMap.put("delete*", nestedTx);
- txMap.put("create*", nestedTx);
-
- source.setNameMap( txMap );
-
- return source;
- }
-
- /**切面拦截规则 参数会自动从容器中注入*/
- @Bean
- public AspectJExpressionPointcutAdvisor pointcutAdvisor(TransactionInterceptor txInterceptor){
- AspectJExpressionPointcutAdvisor pointcutAdvisor = new AspectJExpressionPointcutAdvisor();
- pointcutAdvisor.setAdvice(txInterceptor);
- pointcutAdvisor.setExpression(AOP_POINTCUT_EXPRESSION);
- return pointcutAdvisor;
- }
-
- /*事务拦截器*/
- @Bean("txInterceptor")
- TransactionInterceptor getTransactionInterceptor(PlatformTransactionManager tx){
- return new TransactionInterceptor(tx , transactionAttributeSource()) ;
- }
- }
-
- @Log4j2
- @Component
- @Transactional(propagation = Propagation.REQUIRES_NEW)
- public class TestListener {
-
- @Async
- @TransactionalEventListener(phase= TransactionPhase.BEFORE_COMMIT)
- public void beforeCommitEvent(TestEvent event){
- log.info("测试事件-事务提交前事件:【{}】",event.getStr());
- }
-
- @Async
- @TransactionalEventListener(phase= TransactionPhase.AFTER_COMMIT)
- public void afterCommitEvent(TestEvent event){
- log.info("测试事件-事务提交成功事件:【{}】",event.getStr());
- }
-
- @Async
- @TransactionalEventListener(phase= TransactionPhase.AFTER_ROLLBACK)
- public void afterRollbackEvent(TestEvent event){
- log.info("测试事件-事务回滚之后事件:【{}】",event.getStr());
- }
-
- @Async
- @TransactionalEventListener(phase= TransactionPhase.AFTER_COMPLETION)
- public void afterCompletionEvent(TestEvent event){
- log.info("测试事件-事务完成之后事件:【{}】",event.getStr());
- }
-
- }
-
- public void updateInfo(Integer id) {
- SystemUserCommonResult user = new SystemUserCommonResult();
- user.setId(0);
- user.setName("系统");
-
- CommentInfoForm form = new CommentInfoForm();
- form.setDataType(10);
- form.setRelationId(5);
- form.setContent("测试事务回滚01");
-
- LocalDateTime now = now();
- CommentInfo record = new CommentInfo();
- record.setContent(StringUtils.trimToEmpty(form.getContent()));
- record.setDataType(form.getDataType());
- record.setRelationId(form.getRelationId());
- record.setCreateDate(now);
- record.setCreatorId(user.getId());
- record.setCreator(user.getName());
- commentInfoMapper.insert(record);
- //手动回滚事务
- TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
-
- //推送测试事件
- applicationContext.publishEvent(new TestEvent(this,"测试事务回滚代码执行后"));
-
- }
-
启用Druid的Sping的监控
- #dataSource Pool configuration for DruidDataSource
- spring.datasource.url=jdbc:mysql://xxxxx
- spring.datasource.username=xxx
- spring.datasource.password=xxxxx
- spring.datasource.driver-class-name=com.mysql.jdbc.Driver
-
- #连接池配置
- spring.datasource.druid.initialSize=5
- spring.datasource.druid.minIdle=5
- spring.datasource.druid.maxActive=20
- spring.datasource.druid.maxWait=60000
- spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
- spring.datasource.druid.minEvictableIdleTimeMillis=300000
- spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL
- spring.datasource.druid.testWhileIdle=true
- spring.datasource.druid.testOnBorrow=false
- spring.datasource.druid.testOnReturn=false
- spring.datasource.druid.poolPreparedStatements=true
- spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=20
- spring.datasource.druid.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
- spring.datasource.druid.useGlobalDataSourceStat=true
- ## 使用那些过滤器,支持stat,wall,log4j2等
- ### stat : StatFilter 用于统计监控信息。
- ### wall : wallfilter SQL防火墙。
- spring.datasource.druid.filters=stat,wall,log4j2
-
- ## druid 监控 WebStatFilter配置,说明请参考Druid Wiki,配置_配置WebStatFilter
- spring.datasource.druid.web-stat-filter.enabled=true
- spring.datasource.druid.web-stat-filter.url-pattern=/*
- spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*
-
- ## druid 监控页面 StatViewServlet配置,说明请参考Druid Wiki,配置_StatViewServlet配置
- spring.datasource.druid.stat-view-servlet.enabled=true
- spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
- ##监控页面用户名和密码
- spring.datasource.druid.stat-view-servlet.login-username=xxxx
- spring.datasource.druid.stat-view-servlet.login-password=xxxx
- ##禁用HTML页面上的“Reset All”功能
- spring.datasource.druid.stat-view-servlet.reset-enable=true
- ##IP白名单 (没有配置或者为空,则允许所有访问)
- spring.datasource.druid.stat-view-servlet.allow=
- ##IP黑名单 (存在共同时,deny优先于allow)
- spring.datasource.druid.stat-view-servlet.deny=
- ## Spring监控配置,说明请参考Druid Github Wiki,配置_Druid和Spring关联监控配置
- ### Spring监控AOP切入点,如x.y.z.service.*,配置多个英文逗号分隔
- spring.datasource.druid.aop-patterns= com.abc.service.*
-
修改Spring的日志的级别DEBUG,用于输出事务回滚的日志
控制台输出的日志
-
- [2021-10-27 13:32:53.141] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:372] - Creating new transaction with name [com.abc.service.impl.TestServiceImpl.updateInfo]: PROPAGATION_NESTED,ISOLATION_DEFAULT,timeout_3600,-java.lang.Exception
- [2021-10-27 13:32:53.162] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10005} pool-connect
- [2021-10-27 13:32:53.163] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:264] - Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@f7c20c] for JDBC transaction
- [2021-10-27 13:32:53.167] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:281] - Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@f7c20c] to manual commit
- [2021-10-27 13:32:53.167] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10005} setAutoCommit false
- [2021-10-27 13:32:53.184] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:447] - Creating nested transaction with name [com.abc.service.impl.TestServiceImpl.updateInfo]
- [2021-10-27 13:32:53.213] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn 10005} setSavepoint-SAVEPOINT_1
- [2021-10-27 13:32:53.239] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.m.s.SqlSessionUtils:97] - Creating a new SqlSession
- [2021-10-27 13:32:53.247] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.m.s.SqlSessionUtils:128] - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a08e603]
- [2021-10-27 13:32:53.255] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.m.s.t.SpringManagedTransaction:87] - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@f7c20c] will be managed by Spring
- [2021-10-27 13:32:53.260] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [c.e.m.d.C.insert:159] - ==> Preparing: insert into `comment_info` ( `id`, `data_type`, `relation_id`, `create_date`, `creator`, `creator_id`, `content` ) values ( ?, ?, ?, ?, ?, ?, ? )
- [2021-10-27 13:32:53.416] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Statement:137] - {conn-10005, pstmt-20000} created. insert into `comment_info` (
-
- `id`,
- `data_type`,
- `relation_id`,
- `create_date`,
- `creator`,
- `creator_id`,
- `content`
-
- )
- values (
- ?,
- ?,
- ?,
- ?,
- ?,
- ?,
- ?
- )
- [2021-10-27 13:32:53.438] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [c.e.m.d.C.insert:159] - ==> Parameters: null, 10(Integer), 5(Integer), 2021-10-27 13:32:53.2346418(Timestamp), 系统(String), 0(Integer), java.io.StringReader@60487067(StringReader)
- [2021-10-27 13:32:53.439] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Statement:137] - {conn-10005, pstmt-20000} Parameters : [null, 10, 5, 2021-10-27 13:32:53.2346418, 系统, 0, java.io.StringReader@60487067]
- [2021-10-27 13:32:53.439] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Statement:137] - {conn-10005, pstmt-20000} Types : [INTEGER, INTEGER, INTEGER, TIMESTAMP, VARCHAR, INTEGER, OTHER]
- [2021-10-27 13:32:53.463] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Statement:137] - {conn-10005, pstmt-20000} executed. 23.7545 millis. insert into `comment_info` (
-
- `id`,
- `data_type`,
- `relation_id`,
- `create_date`,
- `creator`,
- `creator_id`,
- `content`
-
- )
- values (
- ?,
- ?,
- ?,
- ?,
- ?,
- ?,
- ?
- )
- [2021-10-27 13:32:53.464] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [c.e.m.d.C.insert:159] - <== Updates: 1
- [2021-10-27 13:32:53.464] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Statement:137] - {conn-10005, pstmt-20000} clearParameters.
- [2021-10-27 13:32:53.464] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [c.a.d.p.PreparedStatementPool:129] - {conn-10005, pstmt-20000} enter cache
- [2021-10-27 13:32:53.465] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.m.s.SqlSessionUtils:186] - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a08e603]
- [2021-10-27 13:32:53.466] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:700] - Transactional code has requested rollback
- [2021-10-27 13:32:55.759] [] [idle_connection_reaper] DEBUG: [o.a.h.i.c.PoolingHttpClientConnectionManager:428] - Closing expired connections
- [2021-10-27 13:32:55.759] [] [idle_connection_reaper] DEBUG: [o.a.h.i.c.PoolingHttpClientConnectionManager:421] - Closing connections idle longer than 60000 MILLISECONDS
- [2021-10-27 13:32:56.252] [] [Connection evictor] DEBUG: [o.a.h.i.c.PoolingHttpClientConnectionManager:428] - Closing expired connections
- [2021-10-27 13:32:56.486] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:830] - Rolling back transaction to savepoint
- [2021-10-27 13:32:56.500] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn 10005} rollback -> SAVEPOINT_1
- [2021-10-27 13:32:56.500] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.b.f.s.DefaultListableBeanFactory:255] - Returning cached instance of singleton bean 'testListener'
- [2021-10-27 13:32:56.504] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.b.f.s.DefaultListableBeanFactory:255] - Returning cached instance of singleton bean 'transactionManager'
- [2021-10-27 13:32:56.504] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:422] - Suspending current transaction, creating new transaction with name [com.abc.listener.TestListener.beforeCommitEvent]
- [2021-10-27 13:32:56.504] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.m.s.SqlSessionUtils:251] - Transaction synchronization suspending SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a08e603]
- [2021-10-27 13:32:56.514] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} pool-connect
- [2021-10-27 13:32:56.515] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:264] - Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6] for JDBC transaction
- [2021-10-27 13:32:56.516] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:281] - Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6] to manual commit
- [2021-10-27 13:32:56.516] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} setAutoCommit false
- [2021-10-27 13:32:56.532] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:743] - Initiating transaction commit
- [2021-10-27 13:32:56.532] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:326] - Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6]
- [2021-10-27 13:32:56.533] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [o.s.j.d.DataSourceTransactionManager:372] - Creating new transaction with name [com.abc.listener.TestListener.beforeCommitEvent]: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; ''
- [2021-10-27 13:32:56.541] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} commited
- [2021-10-27 13:32:56.541] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} setAutoCommit true
- [2021-10-27 13:32:56.543] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [d.s.Connection:132] - {conn-10003} pool-connect
- [2021-10-27 13:32:56.543] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [o.s.j.d.DataSourceTransactionManager:264] - Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1fa1e475] for JDBC transaction
- [2021-10-27 13:32:56.543] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [o.s.j.d.DataSourceTransactionManager:281] - Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1fa1e475] to manual commit
- [2021-10-27 13:32:56.543] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [d.s.Connection:132] - {conn-10003} setAutoCommit false
- [2021-10-27 13:32:56.557] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] INFO : [c.e.l.TestListener:24] - 测试事件-事务提交前事件:【测试业务】
- [2021-10-27 13:32:56.557] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [o.s.j.d.DataSourceTransactionManager:743] - Initiating transaction commit
- [2021-10-27 13:32:56.557] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [o.s.j.d.DataSourceTransactionManager:326] - Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1fa1e475]
- [2021-10-27 13:32:56.559] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:384] - Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6] after transaction
- [2021-10-27 13:32:56.559] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceUtils:340] - Returning JDBC Connection to DataSource
- [2021-10-27 13:32:56.560] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} pool-recycle
- [2021-10-27 13:32:56.560] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:1011] - Resuming suspended transaction after completion of inner transaction
- [2021-10-27 13:32:56.560] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.m.s.SqlSessionUtils:264] - Transaction synchronization resuming SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a08e603]
- [2021-10-27 13:32:56.560] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.m.s.SqlSessionUtils:284] - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a08e603]
- [2021-10-27 13:32:56.560] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.m.s.SqlSessionUtils:310] - Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a08e603]
- [2021-10-27 13:32:56.560] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.m.s.SqlSessionUtils:315] - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a08e603]
- [2021-10-27 13:32:56.560] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:743] - Initiating transaction commit
- [2021-10-27 13:32:56.560] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:326] - Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@f7c20c]
- [2021-10-27 13:32:56.566] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [d.s.Connection:132] - {conn-10003} commited
- [2021-10-27 13:32:56.566] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [d.s.Connection:132] - {conn-10003} setAutoCommit true
- [2021-10-27 13:32:56.570] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10005} commited
- [2021-10-27 13:32:56.570] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.b.f.s.DefaultListableBeanFactory:255] - Returning cached instance of singleton bean 'testListener'
- [2021-10-27 13:32:56.570] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:422] - Suspending current transaction, creating new transaction with name [com.abc.listener.TestListener.afterCommitEvent]
- [2021-10-27 13:32:56.570] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} pool-connect
- [2021-10-27 13:32:56.570] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:264] - Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6] for JDBC transaction
- [2021-10-27 13:32:56.571] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:281] - Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6] to manual commit
- [2021-10-27 13:32:56.571] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} setAutoCommit false
- [2021-10-27 13:32:56.581] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:743] - Initiating transaction commit
- [2021-10-27 13:32:56.581] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:326] - Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6]
- [2021-10-27 13:32:56.581] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [o.s.j.d.DataSourceTransactionManager:372] - Creating new transaction with name [com.abc.listener.TestListener.afterCommitEvent]: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; ''
- [2021-10-27 13:32:56.584] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [o.s.j.d.DataSourceTransactionManager:384] - Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1fa1e475] after transaction
- [2021-10-27 13:32:56.584] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [o.s.j.d.DataSourceUtils:340] - Returning JDBC Connection to DataSource
- [2021-10-27 13:32:56.584] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] DEBUG: [d.s.Connection:132] - {conn-10003} pool-recycle
- [2021-10-27 13:32:56.589] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} commited
- [2021-10-27 13:32:56.589] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} setAutoCommit true
- [2021-10-27 13:32:56.589] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [d.s.Connection:132] - {conn-10002} pool-connect
- [2021-10-27 13:32:56.590] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [o.s.j.d.DataSourceTransactionManager:264] - Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@2a2e613c] for JDBC transaction
- [2021-10-27 13:32:56.590] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [o.s.j.d.DataSourceTransactionManager:281] - Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@2a2e613c] to manual commit
- [2021-10-27 13:32:56.590] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [d.s.Connection:132] - {conn-10002} setAutoCommit false
- [2021-10-27 13:32:56.600] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] INFO : [c.e.l.TestListener:30] - 测试事件-事务提交成功事件:【测试业务】
- [2021-10-27 13:32:56.600] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [o.s.j.d.DataSourceTransactionManager:743] - Initiating transaction commit
- [2021-10-27 13:32:56.601] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [o.s.j.d.DataSourceTransactionManager:326] - Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@2a2e613c]
- [2021-10-27 13:32:56.607] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:384] - Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6] after transaction
- [2021-10-27 13:32:56.607] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceUtils:340] - Returning JDBC Connection to DataSource
- [2021-10-27 13:32:56.607] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} pool-recycle
- [2021-10-27 13:32:56.607] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:1011] - Resuming suspended transaction after completion of inner transaction
- [2021-10-27 13:32:56.607] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.b.f.s.DefaultListableBeanFactory:255] - Returning cached instance of singleton bean 'testListener'
- [2021-10-27 13:32:56.607] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:422] - Suspending current transaction, creating new transaction with name [com.abc.listener.TestListener.afterCompletionEvent]
- [2021-10-27 13:32:56.607] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} pool-connect
- [2021-10-27 13:32:56.608] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:264] - Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6] for JDBC transaction
- [2021-10-27 13:32:56.608] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:281] - Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6] to manual commit
- [2021-10-27 13:32:56.608] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} setAutoCommit false
- [2021-10-27 13:32:56.610] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [d.s.Connection:132] - {conn-10002} commited
- [2021-10-27 13:32:56.611] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [d.s.Connection:132] - {conn-10002} setAutoCommit true
- [2021-10-27 13:32:56.618] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:743] - Initiating transaction commit
- [2021-10-27 13:32:56.618] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:326] - Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6]
- [2021-10-27 13:32:56.619] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [o.s.j.d.DataSourceTransactionManager:372] - Creating new transaction with name [com.abc.listener.TestListener.afterCompletionEvent]: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; ''
- [2021-10-27 13:32:56.619] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [d.s.Connection:132] - {conn-10003} pool-connect
- [2021-10-27 13:32:56.619] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [o.s.j.d.DataSourceTransactionManager:264] - Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1fa1e475] for JDBC transaction
- [2021-10-27 13:32:56.619] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [o.s.j.d.DataSourceTransactionManager:281] - Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1fa1e475] to manual commit
- [2021-10-27 13:32:56.619] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [d.s.Connection:132] - {conn-10003} setAutoCommit false
- [2021-10-27 13:32:56.627] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} commited
- [2021-10-27 13:32:56.628] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} setAutoCommit true
- [2021-10-27 13:32:56.628] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] INFO : [c.e.l.TestListener:42] - 测试事件-事务完成之后事件:【测试业务】
- [2021-10-27 13:32:56.628] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [o.s.j.d.DataSourceTransactionManager:743] - Initiating transaction commit
- [2021-10-27 13:32:56.628] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [o.s.j.d.DataSourceTransactionManager:326] - Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1fa1e475]
- [2021-10-27 13:32:56.629] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [o.s.j.d.DataSourceTransactionManager:384] - Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@2a2e613c] after transaction
- [2021-10-27 13:32:56.629] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [o.s.j.d.DataSourceUtils:340] - Returning JDBC Connection to DataSource
- [2021-10-27 13:32:56.630] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] DEBUG: [d.s.Connection:132] - {conn-10002} pool-recycle
- [2021-10-27 13:32:56.637] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [d.s.Connection:132] - {conn-10003} commited
- [2021-10-27 13:32:56.637] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [d.s.Connection:132] - {conn-10003} setAutoCommit true
- [2021-10-27 13:32:56.646] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:384] - Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@277226d6] after transaction
- [2021-10-27 13:32:56.646] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceUtils:340] - Returning JDBC Connection to DataSource
- [2021-10-27 13:32:56.646] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10004} pool-recycle
- [2021-10-27 13:32:56.646] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:1011] - Resuming suspended transaction after completion of inner transaction
- [2021-10-27 13:32:56.646] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10005} setAutoCommit true
- [2021-10-27 13:32:56.655] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [o.s.j.d.DataSourceTransactionManager:384] - Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1fa1e475] after transaction
- [2021-10-27 13:32:56.655] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [o.s.j.d.DataSourceUtils:340] - Returning JDBC Connection to DataSource
- [2021-10-27 13:32:56.655] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] DEBUG: [d.s.Connection:132] - {conn-10003} pool-recycle
- [2021-10-27 13:32:56.666] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:384] - Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@f7c20c] after transaction
- [2021-10-27 13:32:56.666] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceUtils:340] - Returning JDBC Connection to DataSource
- [2021-10-27 13:32:56.666] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10005} pool-recycle
- [2021-10-27 13:32:56.726] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.w.s.m.m.a.HttpEntityMethodProcessor:277] - Written [ResponseResult(code=0, message=成功, result=null, results=null, errors=null)] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@7220a594]
- [2021-10-27 13:32:56.727] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.w.s.DispatcherServlet:1076] - Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
- [2021-10-27 13:32:56.727] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.w.s.DispatcherServlet:1004] - Successfully completed request
-
从日志中可以看到事务的创建、回滚的以及触发事务事件的全部过程。
创建nested
的顶层事务的日志
- [2021-10-27 13:32:53.141] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:372] - Creating new transaction with name [com.abc.service.impl.TestServiceImpl.updateInfo]: PROPAGATION_NESTED,ISOLATION_DEFAULT,timeout_3600,-java.lang.Exception
- [2021-10-27 13:32:53.162] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10005} pool-connect
- [2021-10-27 13:32:53.163] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:264] - Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@f7c20c] for JDBC transaction
- [2021-10-27 13:32:53.167] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:281] - Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@f7c20c] to manual commit
- [2021-10-27 13:32:53.167] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn-10005} setAutoCommit false
对应的调用链 org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
-> org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary
-> org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
-> org.springframework.transaction.support.AbstractPlatformTransactionManager#doBegin
具体的代码分析后面再补,这里就不展开。
创建nested
的第二层事务的日志
- [2021-10-27 13:32:53.184] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:447] - Creating nested transaction with name [com.expertsplatform.service.impl.TestServiceImpl.updateInfo]
- [2021-10-27 13:32:53.213] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn 10005} setSavepoint-SAVEPOINT_1
对应的调用链: org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
-> org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary
-> org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
-> org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction
具体的代码分析后面再补,这里就不展开。
事务回滚的日志
- [2021-10-27 13:32:53.465] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.m.s.SqlSessionUtils:186] - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4a08e603]
- [2021-10-27 13:32:53.466] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:700] - Transactional code has requested rollback
-
- [2021-10-27 13:32:56.486] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [o.s.j.d.DataSourceTransactionManager:830] - Rolling back transaction to savepoint
- [2021-10-27 13:32:56.500] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [http-nio-80-exec-10] DEBUG: [d.s.Connection:132] - {conn 10005} rollback -> SAVEPOINT_1
对应的调用链:
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
-> org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning
-> org.springframework.transaction.support.AbstractPlatformTransactionManager#commit
-> org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback
-> org.springframework.transaction.support.AbstractTransactionStatus#rollbackToHeldSavepoint
具体的代码分析后面再补,这里就不展开。
事务事件触发日志
- [2021-10-27 13:32:56.557] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-1] INFO : [c.e.l.TestListener:24] - 测试事件-事务提交前事件:【测试业务】
- [2021-10-27 13:32:56.600] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-2] INFO : [c.e.l.TestListener:30] - 测试事件-事务提交成功事件:【测试业务】
- [2021-10-27 13:32:56.628] [d60c4bd9-5f42-4aff-bbb1-0f11e16890b5] [spring-async-3] INFO : [c.e.l.TestListener:42] - 测试事件-事务完成之后事件:【测试业务】
从日志中就能看出,调用业务方法时,创建了两个事件,回滚里其中一个,最终没有触发事务回滚事件,而是触发了事务提交成功事件。
通过分析源码和运行时的调用栈,发现问题的原因是业务方法被Sping的事务增强了两次,即同一个方法被包了两层Spring的事务。
示意图:
实际的调用栈:
分析Spring的事务处理源码后发现,当出现这种情况时,对于PROPAGATION_NESTED
事务传播类型,如果里层事务是通过TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
手动回滚的,上一层和顶层事务是不会回滚的,回滚的只是执行了该代码的当前层的事务。更上层的事务仍会按照正常成功的事务提交。
通过分析调用栈发现问题的原因是Druid
Spring 监控配置导致的重复增强的处理,这个问题之前在分析Spring Boot循环依赖的问题时也发现了,具体可以参考Spring Boot 2.0.0 升级到2.4.1 循环依赖问题的解决 ,原因是同一个原因,升级Druid的版本就可以了。这个应该是Druid的一个BUG。当然也可以通过不配置spring.datasource.druid.aop-patterns
来禁用Druid的Sping监控来处理。
事务增强的处理逻辑
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
-
- protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
- final InvocationCallback invocation) throws Throwable {
-
- // If the transaction attribute is null, the method is non-transactional.
- TransactionAttributeSource tas = getTransactionAttributeSource();
- final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
- final PlatformTransactionManager tm = determineTransactionManager(txAttr);
- final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
-
- if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
- // Standard transaction demarcation with getTransaction and commit/rollback calls.
- TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
- Object retVal = null;
- try {
- // This is an around advice: Invoke the next interceptor in the chain.
- // This will normally result in a target object being invoked.
- retVal = invocation.proceedWithInvocation();
- }
- catch (Throwable ex) {
- // target invocation exception
- completeTransactionAfterThrowing(txInfo, ex);
- throw ex;
- }
- finally {
- cleanupTransactionInfo(txInfo);
- }
- commitTransactionAfterReturning(txInfo);
- return retVal;
- }
-
- else {
- final ThrowableHolder throwableHolder = new ThrowableHolder();
-
- // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
- try {
- Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
- TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
- try {
- return invocation.proceedWithInvocation();
- }
- catch (Throwable ex) {
- if (txAttr.rollbackOn(ex)) {
- // A RuntimeException: will lead to a rollback.
- if (ex instanceof RuntimeException) {
- throw (RuntimeException) ex;
- }
- else {
- throw new ThrowableHolderException(ex);
- }
- }
- else {
- // A normal return value: will lead to a commit.
- throwableHolder.throwable = ex;
- return null;
- }
- }
- finally {
- cleanupTransactionInfo(txInfo);
- }
- });
-
- // Check result state: It might indicate a Throwable to rethrow.
- if (throwableHolder.throwable != null) {
- throw throwableHolder.throwable;
- }
- return result;
- }
- catch (ThrowableHolderException ex) {
- throw ex.getCause();
- }
- catch (TransactionSystemException ex2) {
- if (throwableHolder.throwable != null) {
- logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
- ex2.initApplicationException(throwableHolder.throwable);
- }
- throw ex2;
- }
- catch (Throwable ex2) {
- if (throwableHolder.throwable != null) {
- logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
- }
- throw ex2;
- }
- }
- }
事务方法执行异常的处理
org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing
- protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
- if (txInfo != null && txInfo.getTransactionStatus() != null) {
- if (logger.isTraceEnabled()) {
- logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
- "] after exception: " + ex);
- }
- //判断异常是否在事务配置的异常捕获范围内,是的话执行回滚操作
- if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
- try {
- txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
- }
- catch (TransactionSystemException ex2) {
- logger.error("Application exception overridden by rollback exception", ex);
- ex2.initApplicationException(ex);
- throw ex2;
- }
- catch (RuntimeException | Error ex2) {
- logger.error("Application exception overridden by rollback exception", ex);
- throw ex2;
- }
- }
- //不是的话,执行提交操作
- else {
- // We don't roll back on this exception.
- // Will still roll back if TransactionStatus.isRollbackOnly() is true.
- try {
- txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
- }
- catch (TransactionSystemException ex2) {
- logger.error("Application exception overridden by commit exception", ex);
- ex2.initApplicationException(ex);
- throw ex2;
- }
- catch (RuntimeException | Error ex2) {
- logger.error("Application exception overridden by commit exception", ex);
- throw ex2;
- }
- }
- }
- }
判断事务是否回滚的处理逻辑
org.springframework.transaction.support.AbstractPlatformTransactionManager#commit
- public final void commit(TransactionStatus status) throws TransactionException {
- if (status.isCompleted()) {
- throw new IllegalTransactionStateException(
- "Transaction is already completed - do not call commit or rollback more than once per transaction");
- }
-
- DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
- // 判断是否手动设置了回滚,即调用了TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
- if (defStatus.isLocalRollbackOnly()) {
- if (defStatus.isDebug()) {
- logger.debug("Transactional code has requested rollback");
- }
- //手动回滚,不是意外的回滚
- processRollback(defStatus, false);
- return;
- }
- // 判断是否需要全局回滚,即整个事务触发异常了,或者手动设置了Connection的回滚标志
- if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
- if (defStatus.isDebug()) {
- logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
- }
- //全局的回滚为意外的回滚
- processRollback(defStatus, true);
- return;
- }
-
- processCommit(defStatus);
- }
作死小技巧: 手动修改事务的isGlobalRollbackOnly
为true
的方法(玩玩可以,别用在实际代码上)
- TransactionStatus transactionStatus = TransactionAspectSupport.currentTransactionStatus();
- if(transactionStatus instanceof DefaultTransactionStatus){
- DefaultTransactionStatus defaultTransactionStatus = (DefaultTransactionStatus)transactionStatus;
- Object object = defaultTransactionStatus.getTransaction();
- if(object instanceof JdbcTransactionObjectSupport){
- JdbcTransactionObjectSupport transactionObject = (JdbcTransactionObjectSupport) object;
- transactionObject.getConnectionHolder().setRollbackOnly();
- }
- }
回滚的处理逻辑
org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback
- private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
- try {
-
- boolean unexpectedRollback = unexpected;
-
- try {
- //触发事务完成前的事件
- triggerBeforeCompletion(status);
-
- //如果当前事务支持部分回滚,则执行部分回滚,即`nested`事务
- if (status.hasSavepoint()) {
- if (status.isDebug()) {
- logger.debug("Rolling back transaction to savepoint");
- }
- status.rollbackToHeldSavepoint();
- }
- // 当前事务是顶层事务的回滚
- else if (status.isNewTransaction()) {
- if (status.isDebug()) {
- logger.debug("Initiating transaction rollback");
- }
- doRollback(status);
- }
- else {
- //异常的事务回滚处理
- // Participating in larger transaction
- if (status.hasTransaction()) {
- if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
- if (status.isDebug()) {
- logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
- }
- //设置全局回滚
- doSetRollbackOnly(status);
- }
- else {
- if (status.isDebug()) {
- logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
- }
- }
- }
- else {
- logger.debug("Should roll back transaction but cannot - no transaction available");
- }
- // Unexpected rollback only matters here if we're asked to fail early
- if (!isFailEarlyOnGlobalRollbackOnly()) {
- unexpectedRollback = false;
- }
- }
- }
- catch (RuntimeException | Error ex) {
- triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
- throw ex;
- }
- //触发事务提交后的事件
- triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
-
-
- // Raise UnexpectedRollbackException if we had a global rollback-only marker
- if (unexpectedRollback) {
- throw new UnexpectedRollbackException(
- "Transaction rolled back because it has been marked as rollback-only");
- }
- }
- finally {
- cleanupAfterCompletion(status);
- }
- }
PROPAGATION_NESTED
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
独立设置是否回滚。当前层的回滚不会影响上层的回滚,但上层的回滚会影响下层的回滚。PROPAGATION_REQUIRED
Transaction rolled back because it has been marked as rollback-only
异常。如果要实现在事务中任意一层回滚了,整个事务全部回滚,在不修改业务逻辑的情况下,如何实现呢?
思路有以下几个:
修改所有的需要手动提交事务的业务方法,当需要手动回滚事务时,方法返回需要回滚事务。
优点
实现简单
缺点
需要修改所有相关的业务方法,并且需要占用业务方法的返回值。
用AOP方法对所有需要事务增强的方法再包一层,增加自定义的否全局回滚的标志,在调用完实际业务方法后,判断全局回滚标志是否开启,是的话,手动设置当前层回滚事务。 同时在所有需要手动回滚的事务方法里,在执行手动回滚事务时,同时修改该当前事务的自定义的全局回滚标志。
优点
实现困难,类似于分布式事务的处理逻辑,需要考虑多线程的问题。
缺点
不影响业务代码,只需对手动回滚业务的操作再封装一层即可。
后续抽空实现以下方案2,看看效果,目前先用方案1
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。