👉 这是一个或许对你有用的社群
🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:
《项目实战(视频)》:从书中学,往事中“练”
《互联网高频面试题》:面朝简历学习,春暖花开
《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题
《精进 Java 学习指南》:系统学习,互联网主流技术栈
《必读 Java 源码专栏》:知其然,知其所以然
👉这是一个或许对你有用的开源项目
国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。
功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号等等功能:
Boot 地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
Cloud 地址:https://gitee.com/zhijiantianya/yudao-cloud
视频教程:https://doc.iocoder.cn
来源:blog.csdn.net/CSDN_WYL2016
/article/details/106767583
@Transactional
是一种基于注解管理事务的方式,spring通过动态代理的方式为目标方法实现事务管理的增强。
@Transactional
使用起来方便,但也需要注意引起@Transactional
失效的场景,本文总结了七种情况,下面进行逐一分析。
1、异常被捕获后没有抛出
当异常被捕获后,并且没有再抛出,那么deleteUserA
是不会回滚的。
@Transactional
public void deleteUser() {
userMapper.deleteUserA();
try {
int i = 1 / 0;
userMapper.deleteUserB();
} catch (Exception e) {
e.printStackTrace();
}
}
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
视频教程:https://doc.iocoder.cn/video/
2、抛出非运行时异常
异步虽然抛出了,但是抛出的是非RuntimeException
类型的异常,依旧不会生效。
@Transactional
public void deleteUser() throws MyException{
userMapper.deleteUserA();
try {
int i = 1 / 0;
userMapper.deleteUserB();
} catch (Exception e) {
throw new MyException();
}
}
如果指定了回滚异常类型为Exception,那么就可以回滚非RuntimeException
类型异常了。
@Transactional(rollbackFor = Exception.class)
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/yudao-cloud
视频教程:https://doc.iocoder.cn/video/
3、方法内部直接调用
如果先调用deleteUser()
,那么deleteUserA()
是不会回滚的,其原因就是@Transactional
根本没生成代理,如果直接调用deleteUser2()
那么没问题,deleteUserA()
会回滚。
public void deleteUser() throws MyException{
deleteUser2();
}
@Transactional
public void deleteUser2() throws MyException{
userMapper.deleteUserA();
int i = 1 / 0;
userMapper.deleteUserB();
}
修改方式,把当前类自己注入一下调用即可。
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
//自己注入自己
@Autowired
UserService userService;
public void deleteUser() throws MyException{
userService.deleteUser2();
}
@Transactional
public void deleteUser2() throws MyException{
userMapper.deleteUserA();
int i = 1 / 0;
userMapper.deleteUserB();
}
}
4、新开启一个线程
如下的方式deleteUserA()
也不会回滚,因为spring实现事务的原理是通过ThreadLocal把数据库连接绑定到当前线程中,新开启一个线程获取到的连接就不是同一个了。
@Transactional
public void deleteUser() throws MyException{
userMapper.deleteUserA();
try {
//休眠1秒,保证deleteUserA先执行
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
int i = 1/0;
userMapper.deleteUserB();
}).start();
}
5、注解到private方法上
idea直接会给出提示Methods annotated with ‘@Transactional’ must be overridable
,原理很简单,private修饰的方式,spring无法生成动态代理。
@Transactional
private void deleteUser() throws MyException{
userMapper.deleteUserA();
int i = 1/0;
userMapper.deleteUserB();
}
6、数据库本身不支持
mysql数据库,必须设置数据库引擎为InnoDB。
7、事务传播属性设置错误
注意传播属性的设置,比如设置了:PROPAGATION_NOT_SUPPORIED
(以非事务的方式执行,如果当前有事务则把当前事务挂起)。
欢迎加入我的知识星球,全面提升技术能力。
👉 加入方式,“长按”或“扫描”下方二维码噢:
星球的内容包括:项目实战、面试招聘、源码解析、学习路线。
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)