目录
参考了抖音上的代文前辈和某位的图,(很久之前就总结了但是没做笔记)在网吧写的总结(绝对个人心得),没办法了
1.方法的访问类型不是public
当事务方法访问的修饰符为非public时 ,会导致事务失效
java的访问权限有四种:private、default、protected、public,权限从左到右以此变大,如果我们这个addUser方法变为了其他的访问类型就会失效
@Service
public class EmpService {
@Transactional
private void addUser(UserModel userModel){
saveData(userModel);
}
我们来看看Spring的源码怎么解释的,如果非public目标方法。则TransactionAttribute返回null,不支持事务
2.不能为final
那么问题来了,如果我这个方法骚一点,public+final修饰呢?
当方法被final修饰时也会导致事务失效
解释:众所周知,我们的Spring事务底层用的还是AOP(本质也就是动态代理,也就是反射,涉及的知识点还是很多的),用的jdk动态代理或者cglib(这个到时候我会单独再说一下,一个本质实现接口,一个本质是继承父类重写实现)——>来帮我们生成代理类,在代理类中实现事务功能;
总结:所以说,你都final了,怎么重写该方法或者说实现该方法呢?,所以说不能添加事务了嘛
深入需要结合JVM类加载思考+里面的Klass模型(里面说的有些有问题我没改,太懒了),这里diss一些看个Spring视频就到处说的人,低调点哈哈啊哈哈
Java反射感悟_Fairy要carry的博客-CSDN博客
类加载一些理解(—)_Fairy要carry的博客-CSDN博客
JVM-字节码是如何被JVM执行的+关于线程一点思想引子_Fairy要carry的博客-CSDN博客_jvm如何执行字节码
JVM内存模型与本地内存_Fairy要carry的博客-CSDN博客_jvm内存和本地内存
@Service
public class EmpService {
@Transactional
public final void addUser(UserModel user){
save(user);
}
}
3.循环调用和未被Spring管理
切记方法之间的循环调用,之前写demo也出现这样的情况,如果说一个方法被Spring管理了,那么他如果有另一个相互调用的方法在里面,另一个相互调用的方法切记不能被Spring管理,和循环依赖一个意思;
另外,未被Spring管理也会失效
4.注意多线程场景
如果说在一个事务方法add中,有另一个线程调用了这个事务方法add里面的一个update方法,也会出现add事务失效,因为是两个不同的线程执行两个方法,那么获取的数据库连接就是不一样,从而就是两个不同的事务,如果update方法中抛出了异常,add方法是不会回滚的
@Service
public class EmpService {
@Autowired
private OrderService orderService;
@Transactional
public void add(User user){
new Thread(()->{
orderService.update();
}).start();
}
}
@Service
public class OrderService{
@Transactional
public void update(){
System.out.println("======================");
}
}
注: spring的事务是通过数据库的连接来实现的。当前线程中保存了一个map,key是数据源,value是数据库连接。同一个事务,指同一个数据库连接,只有拥有同一个事务连接才能保证同时提交和回滚。如果是不同的线程,拿到的数据库连接肯定是不同的。
5.数据库的引擎
当数据库的引擎用的不是innodb也会造成事务失效
6.事务方法中抛出的异常被捕获
解释: 简单来说,我们如果在@Trancational事务方法中手动对异常进行处理了,那么我们的Spring不就会没接收到异常嘛,那么我们还怎么进行回滚呢——>throw解决,而且最好是抛出RuntimeException,不然效率比较低,为什么呢?
其实如果你深入理解过反射和类加载的话就知道,因为我们的事务本质其实基于反射实现的动态代理思想来的,目的就是通过反射reflect包下的里面的InvocationHandler接口中的invoke方法执行我们的目标类方法进而进行增强——>本质也就是通过我们目标类的类对象对方法区中的成员方法进行调用(在Java代码比如:xxx.class.getMethod(),从而得到我们的Method,进而invoke执行就完了)
7.事务传播机制
如果事务的传播特性设置错了,事务也会失效。如下:propagation = Propagation.NEVER这种类型的传播特性不支持事务,如果有事务会抛出异常。
目前只有这三种传播特性才会创建新事物:REQUIRED、REQUIRES_NEW、NESTED
@Service
public class EmpService {
@Transactional(propagation = Propagation.NEVER)
public void add(User user){
saveData(user);
updateSataus(user);
}
}
8.嵌套事务现象
如果说对于这样的代码。如果saveData执行成功了,updataStatus执行失败了,那么整个add方法就会回滚。那么之前saveData执行成功的这个方法也会回滚
@Service
public class EmpService {
@Autowired
private OrderService orderService;
@Transactional
public void add(User user) {
saveData(user);
orderService.updateSataus();
}
}
@Service
public class OrderService(){
@Transactional(propagation = Propagation.NESTED)
public void updateSataus(){
System.out.println("======================");
}
}
如果我们目的是只让报错的方法回滚呢?(但是一般业务场景下,貌似都回滚较多,本人写的都是这样,类似seata的XA模式,直接强一致了)
@Service
public class EmpService {
@Autowired
private OrderService orderService;
@Transactional
public void add(UserModel userModel) {
saveData(userModel);
try {
orderService.updateSataus();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Service
public class OrderService(){
@Transactional(propagation = Propagation.NESTED)
public void updateSataus(){
System.out.println("======================");
}
}