文章目录
🗯️ 上节回顾:上一节中,我们集成第三方组件,比如 MyBatisPlus。
👉 本节目标:实现事务集成,比如 本地事务、分布式事务。【使用多数据源的你一定会遇到事务问题】
🚀 本节内容:
- 本地事务集成【支持力度有限,最好使用分布式事务 😂】
- 重点 1:直接使用 @DSTransactional 注解
- 重点 2: 不支持与 Spring 事务共用!!!比如 @Transactional 注解
- 重点 3:只适合本地多数据源场景,涉及异步及微服务等完整事务场景,请参考 Seata 方案
- 重点 4:@DSTransactional 功能有限,不支持更多配置,比如只读等
- Seata 分布式事务集成【重点 😊】
- 重点 1:spring.datasource.dynamic.seata 为 true 开启,或者具体数据源配置 seata 属性开启
- 重点 2:seata.enable-auto-data-source-proxy 务必设置为 false,关闭 Seata 数据源自动代理
- 重点 3:使用 @DS 注解即可
事务处理
原生 JDBC 事务处理
try {
connection.setAutoCommit(false);
// 这里用connection对数据库做了一系列操作,CRUD
connection.commit();//没有异常就提交
} catch (Exception ex) {
connection.rollback(); //异常回滚
} finally {
connection.setAutoCommit( true);
}
以上是事务的核心,所有操作要一起成功,只要出错就回滚。
Spring 事务处理
Spring 开启事务很简单,在需要事务的方法和类上添加 @Transactional 注解。
@Transactional
public void test() {
Aservice.dosometing();
Bservice.dosometing();
}
使用了 @Transactional,**Spring 会保证整个事务下都复用同一个 connection。**在默认配置下,只要事务中发生RuntimeException,就会回滚。
本地事务
多数据源事务方案的解决方案:
- 利用 atomiks 手动构建多数据源事务,适合数据源较少,配置的参数也不太多,性能要求不高的项目。难点就是手动配置量大,需要耗费一定时间。
- 用 seata 类似的分布式事务解决方案,难点就是需要搭建维护如 seata-server 的统一管理中心。
基础介绍
自从 3.3.0 开始,由 seata 的核心贡献者 https://github.com/a364176773 贡献了基于 connection 代理的方案。建议从3.4.0版本开始使用,其修复了一个功能,老版本不加 @DS 只加 @DSTransactional 会报错。
注意事项
本地事务实现很简单,就是循环提交,发生错误,循环回滚。 我们默认的前提是数据库本身不会异常,比如宕机。如数据在回滚的过程突然宕机,本地事务就会有问题。如果你需要完整分布式方案请使用seata方案。
- 不支持spring原生事务,不支持spring事务,不支持spring事务,可分别使用,千万不能混用。
- 再次强调不支持 spring 事务注解,可理解成独立写了一套事务方案。
- 只适合简单本地多数据源场景, 如果涉及异步和微服务等完整事务场景,请使用 seata 方案。
- 暂时不支持更多配置,如只读,如 spring 的传播特性。 后续会根据反馈考虑支持。4.1.4会开始支持在类上使用.
使用方法
在最外层的方法添加 @DSTransactional,底下调用的各个类该切数据源就正常使用DS切换数据源即可。
//如AService调用BService和CService的方法,A,B,C分别对应不同数据源。
public class AService {
@DS("a")//如果a是默认数据源则不需要DS注解。
@DSTransactional
public void dosomething(){
BService.dosomething();
CService.dosomething();
}
}
public class BService {
@DS("b")
public void dosomething(){
//dosomething
}
}
public class CService {
@DS("c")
public void dosomething(){
//dosomething
}
}
只要 @DSTransactional 注解下任一环节发生异常,则全局多数据源事务回滚。如果BC上也有 @DSTransactional 会有影响吗?没有影响的。
示例项目
https://github.com/dynamic-datasource/dynamic-datasource-samples/tree/master/tx-samples/tx-local-sample 完整示例项目 数据库都已准备好,可以直接运行测试。http://localhost:8080/doc.html
示例项目A,B,C分别对应OrderService,ProductService,AccountService。分别是独立的数据库。
用户下单分别调用产品库扣库存,账户库扣余额。
如果库存不足,或用户余额不足都抛出RuntimeException,触发整体回滚。
@Slf4j
@Service
@AllArgsConstructor
public class OrderService {
private final OrderMapper orderMapper;
private final AccountService accountService;
private final ProductService productService;
//@DS("order") 这里不需要,因为order是默认库,如果开启事务的不是默认库则必须加
@DSTransactional //注意这里开启事务
public void placeOrder(PlaceOrderRequest request) {
log.info("=============ORDER START=================");
Long userId = request.getUserId();
Long productId = request.getProductId();
Integer amount = request.getAmount();
log.info("收到下单请求,用户:{}, 商品:{},数量:{}", userId, productId, amount);
log.info("当前 XID: {}", TransactionContext.getXID());
Order order = Order.builder()
.userId(userId)
.productId(productId)
.status(