01
—
背景
02
—
版本选择
Spring Boot版本支持MongoDB事务管理要求:
Mongodb 4.0副本集群、Mongodb 4.2支持分片集群事务(必须)
spring.data.mongodb 版本2.1以上(必须)
Spring Boot版本2.1以上(必须)
目前业务中使用的是Mongodb 4.0及4.0以上版本,因此只需升级Spring Boot、spring.data.mongodb即可。
为了在支持MongoDB事务管理的基础上尽量减少项目代码修改范围,因此版本选择如下:
spring.data.mongodb:2.1.15.RELEASE
springboot:2.1.12.RELEASE
03
—
版本特征
默认动态代理策略
默认使用CGLIB动态代理,包括AOP。如果需要基于接口的动态代理, 需要设置spring.aop.proxy-target-class属性为false。
WebMvcConfigurerAdapter这个抽象类已经过时,可以用WebMvcConfigurer替代。
// 1.5.12.RELEASE
public class MyWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
// ...
}
// 2.1.12.RELEASE
public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer {
// ...
}
默认的数据库连接池由Tomcat换成HikariCP。性能方面 HikariCP > Druid > tomcat-jdbc > dbcp > c3p0
如果在一个Tomcat应用中用spring.datasource.type来强制使用Hikari连接池, 则可以去掉这个override。
当使用spring-boot-starter-redis的时候,Lettuce现已取代Jedis作为Redis驱动。仍然支持Jedis,并且你可以任意切换依赖机制,通过排除io.lettuce:lettuce-core和添加redis.clients.jedis的方式。
以下库的最低支持版本:
Elasticsearch 5.6
Gradle 4
Hibernate 5.2
Jetty 9.4
Spring Framework 5
Spring Security 5
Tomcat 8.5
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.1-Release-Notes
04
—
升级步骤
<parent>
<groupId>suishen-webx</groupId>
<artifactId>suishen-webx-parent</artifactId>
<version>2.0-SNAPSHOT</version>
</parent>
(2)完成后确认项目中的依赖版本:
注意:
suishen-webx-parent已将表格中的依赖升级,项目中无需再次手动引入。
其他依赖相匹配的版本,请参照:https://docs.spring.io/spring-boot/docs/2.1.12.RELEASE/reference/pdf/spring-boot-reference.pdf
引入@EnableTransactionManagement
@EnableTransactionManagement
public class MainApplication extends SuishenWebxApplication {
}
(2)配置mongodb事务管理器
方式1:使用applicationContext-mongodb.xml配置:
<!-- 配置mongodb事务管理器 -->
<bean id="transactionManager" class="org.springframework.data.mongodb.MongoTransactionManager">
<constructor-arg name="dbFactory" ref="mongoDbFactory"/>
</bean>
@Configuration
public class TransactionConfig {
@Bean
public MongoTransactionManager transactionManager(MongoDatabaseFactory factory){
return new MongoTransactionManager(factory);
}
}
由于spring.data.mongodb 2.1.15.RELEASE版本在数据库认证时,会先将密码进行URLDecoder.decode校验,因此mongo密码中的%号需要替换成%25。
spring.data.mongodb 1.10.15.RELEASE版本
spring.data.mongodb 2.1.15.RELEASE版本
05
—
使用事务
@SuishenLog(logName = "添加策略")
public StrategyVo addStrategy(StrategyAddReq addReq) {
SuishenUser suishenUser = SecurityContextUtil.getSessionUser();
long now = System.currentTimeMillis();
Strategy strategy = Strategy.builder()
.projectId(addReq.getProjectId())
.strategyName(addReq.getStrategyName())
.strategyDesc(addReq.getStrategyDesc())
.status(addReq.getStatus())
.createUser(suishenUser.getNickName())
.createTime(now)
.updateUser(suishenUser.getNickName())
.updateTime(now)
.build();
strategy = strategyService.addStrategy(strategy);
LogContext.instance().appendLog("操作人(%s)", suishenUser.getNickName())
.appendLog("策略id(%s)", strategy.getId());
// 测试代码
if (Objects.nonNull(strategy)) {
throw new BusinessException("添加策略失败");
}
StrategyLog strategyLog = StrategyLog.builder()
.projectId(strategy.getProjectId())
.strategyId(strategy.getId())
.createUser(suishenUser.getNickName())
.createTime(now)
.updateUser(suishenUser.getNickName())
.updateTime(now)
.build();
strategyLogService.addStrategyLog(strategyLog);
return StrategyVo.buildVo(strategy);
}
@SuishenLog(logName = "添加策略事务")
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public StrategyVo addStrategyTest(StrategyAddReq addReq) {
return addStrategy(addReq);
}
事务传播行为:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播
Spring 定义了如下七种传播行为,这里以A业务和B业务之间如何传播事务为例说明:
PROPAGATION_REQUIRED :required , 必须。默认值,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务。
PROPAGATION_SUPPORTS:supports ,支持。A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行。
PROPAGATION_MANDATORY:mandatory ,强制。A如果有事务,B将使用该事务;如果A没有事务,B将抛异常。
PROPAGATION_REQUIRES_NEW :requires_new,必须新的。如果A有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务。
PROPAGATION_NOT_SUPPORTED :not_supported ,不支持。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行。
PROPAGATION_NEVER :never,从不。如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行。
PROPAGATION_NESTED :nested ,嵌套。A和B底层采用保存点机制,形成嵌套事务。
代码示例如下
/**
* 添加策略事务测试
*/
"添加策略事务测试") (logName =
public StrategyVo addStrategyTest2(StrategyAddReq addReq) {
// txTemplate可以和transactionManager进行相同的配置,而不需要每次new,本代码仅作为测试使用
TransactionTemplate txTemplate = new TransactionTemplate(mongoTransactionManager);
return txTemplate.execute(new TransactionCallback<StrategyVo>() {
public StrategyVo doInTransaction(TransactionStatus transactionStatus) {
try {
return addStrategy(addReq);
} catch (Exception e) {
transactionStatus.setRollbackOnly();
}
return null;
}
});
}
执行addStrategy,Strategy新增成功,StrategyLog新增失败
执行addStrategyTest,Strategy新增失败,StrategyLog新增失败
执行addStrategyTest2,Strategy新增失败,StrategyLog新增失败
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:873)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:710)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:534)
作者 | 孙景亮 资深服务端开发工程师