文章目录
- 背景
- 前置环境准备
- 关于 configuration 代码
- 关于 transaction 使用
- 其他
背景
你使用 springboot 和 mybatis/mybatis plus 来进行 web 开发,但是你发现你需要使用到事务操作
前置环境准备
首先你得在 application.yml 中配置好 mysql 数据源,这个我在其他博文中有写
然后你得在 application.yml 中配置好 mybatis 配置,这个我在其他博文中有写
关于 configuration 代码
通常来说,常规业务开发中,application.yml 中配置了一个 mysql 数据源,也配置 mybatis 的常规配置即可,然后想要开启事务操作可以直接在 service 指定方法上方加上 @Transactionl 即可让此方法开通事物操作了,而不用去自定义 Configuration 来配置事物操作
如果你要自定义也可以,或者如果你使用多 mysql 多个数据源,你可能会在 application.yml 中配置多个 mysql 数据源,那么你就需要指定哪个数据源对应哪个事物操作 transactionManager 了,具体看下面代码
// 数据源 1 对应的配置
@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.example.primary.mapper", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "primarySqlSessionFactory")
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
return sessionFactoryBean.getObject();
}
// 事务管理器
@Primary
@Bean(name = "primaryTransactionManager")
public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
// 数据源 2 对应的配置
@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.example.secondary.mapper", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDataSourceConfig {
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondarySqlSessionFactory")
public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
return sessionFactoryBean.getObject();
}
// 事务管理器
@Bean(name = "secondaryTransactionManager")
public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
关于 transaction 使用
@Transactionl 标记的表示默认会使用主数据源的事务管理器
如果你是 mysql 单个数据源,一旦配置好 yml 中的 msyql 和 mybatis 之后,一般对 Service 中的方法上头加上 @Transactional 就可以直接对其开启事务了(通常情况),使用上 @Transactional 表示该方法中如果抛出未捕获的异常时就会触发回滚,这样的异常是 RuntimeException 以及子类或者 error 错误时能回滚,如果是 IOException SQLException 等异常不会回滚。并且方法中如果你 try catch 了这个异常,catch 后不再往外抛出就不会触发回滚,如果再往外抛出还会回滚。发生回滚不只是 try catch 中的,而是整个方法内该 transactionManager 事务管理器对应的数据源的数据库操作都会回滚
@Service
public class SomeService {
@Autowired
private SomeMapper someMapper; // 假设这个Mapper使用的是ds1数据源
@Transactional
public void someTransactionalMethod() {
// 这里的方法调用将会使用ds1数据源的事务管理器
someMapper.updateSomething();
}
}
如果是 mysql 多数据源,一般会像上头配置 Configuration 代码,指定不同数据源对应的事务管理器叫啥,因此你使用事务时候会指定哪个 transactionManager
@Service
public class SomeService {
@Autowired
private SomeMapper someMapper; // 假设这个Mapper使用的是ds1数据源
// 指定 transactionManager
@Transactional(transactionManager = "secondaryTransactionManager")
public void someTransactionalMethod() {
// 这里的方法调用将会使用ds1数据源的事务管理器
someMapper.updateSomething();
}
}
如果你的 servive 中的一个方法会操作多个数据源,这个方法操作抛出异常时候,需要让多个数据都回滚,该怎么做呢?一般业务上会采用分布式事务使用 JTA(Atomikos),我的其他博文有写,安全性低一些的可以考虑使用 Spring 框架提供的一种用于管理多数据源事务的方法 ChainedTransactionManager 来简单替代
其他
如果你不希望 service 方法中抛出异常的时候才回滚,你希望如果走到某种逻辑时候也能回滚,你也可以手动提交事务,等同于替代了 @Transactional
@Service
public class MyService {
@Autowired
private DataSourceTransactionManager transactionManager; // 或者 PlatformTransactionManager
// ... 其他业务方法 ...
public void performManualTransaction() {
// 定义一个事务属性
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("MyManualTransaction");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 获取事务状态
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 执行数据库操作
myMapper.updateSomething();
// 执行其他数据库操作或业务逻辑
// ...
// 如果没有异常,提交事务
transactionManager.commit(status);
} catch (Exception ex) {
// 发生异常时,回滚事务
transactionManager.rollback(status);
// 记录日志或执行其他异常处理逻辑
// ...
throw ex; // 可以选择重新抛出异常或进行其他处理
}
}
}
如果你使用 mybatis 这个 ORM 框架,你可能没有使用 springboot 的能力,那么该怎么做呢?你通过代码方式生成 SqlSession 时候会指定是否要开启事务,对于开启事务的 sqlSession 你需要手动 commit 提交,和 rollback