Spring事务失效的各种场景

Spring事务失效的各种场景一、访问权限JAVA的访问权限主要是:private、default、protected、public,它们的权限则是依次变大 。
如果我们在开发的时候定义错误的访问权限,就会导致事务出现问题
@Servicepublic class DemoService {@Transactionalprivate void query(Demo demo) {}}

Spring事务失效的各种场景

文章插图
 
我们可以查看源码,可以明白,spring事务的实现
AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断,如果目标方法不是public,则TransactionAttribute返回null,即不支持事务 。
二、方法用final修饰我们在学习的时候都知道,某个方法不想被子类继承的时候就会加上关键字final,这种写法正常来说是没有问题的,但是如果该方法被用上事务就不行了
@Servicepublic class DemoService {@Transactionalpublic final void query(Demo demo) {}}这样的话会导致事务的失效,因为spring事务底层实现使用了代理,aop,通过jdk的动态代理或者cglib,生成了代理类,在代理类中实现了事务功能,如果方法被final修饰,无法重写该方法,也就无法添加事务的功能了
 
Spring事务失效的各种场景

文章插图
 
像idea这里就提出了提示,同样的如果方法是static方法也是不行的
 
Spring事务失效的各种场景

文章插图
 
因为静态方法是属于类的,而不是属于对象的,无法重写静态方法所以也就不可能实现事务
三、方法内部调用在同一个类的service中,调用其他的事务方法
@Servicepublic class DemoService {@Transactionalpublicvoid query(Demo demo) {save(demo);}@Transactionalpublic void save(Demo demo) {}}可以看到,query方法调用了save的方法,由于spring的事务实现是因为aop生成代理,这样是直接调用了this对象,所以也不会生成事务 。
解决方法:
1、增加一个service,把一个事务的方法移到新增加的service方法里面,然后进行注入再调用
@Servicepublic class DemoTwoService {@Transactionalpublic void save(Demo demo) {}}@Servicepublic class DemoService {@AutowiredDemoTwoService demoTwoService;@Transactionalpublicvoid query(Demo demo) {demoTwoService.save(demo);}}2、在自己类中注入自己
@Servicepublic class DemoService {@AutowiredDemoService demoService;@Transactionalpublicvoid query(Demo demo) {demoService.save(demo);}@Transactionalpublic void save(Demo demo) {}}由于这种写法基于spring的三级缓存不会导致,循环依赖的问题出现
3、通过AopContentent
@Servicepublic class DemoService {@Transactionalpublicvoid query(Demo demo) {DemoService demoService = (DemoService)AopContext.currentProxy();demoService.save(demo);}@Transactionalpublic void save(Demo demo) {}}四、没有被spring管理在使用spring事务的时候,对象要被spring进行管理,也就是需要创建bean,一般我们都会加@Controller、@Service、@Component、@Repository等注解,可以自动实现bean实例化和依赖注入的功能 。,如果忘记加了,也会导致,事务的失效
五、多线程调用@Servicepublic class DemoService {@AutowiredDemoTwoService demoTwoService;@Transactionalpublicvoid query(Demo demo) {new Thread(()->{demoTwoService.save(demo);}).start();}}通过上面的例子,我们可以知道,query调用了事务方法save,但是事务方法在另一个线程里面调用,这样会导致两个方法在不同的一个线程中,获取的数据库连接也不一样,所以会是两个不同的事务,如果save的方法抛出了异常,query回滚是不可能的,看过spring源码,我们可以知道,spring的事务是通过连接数据库来实现的,当前线程保存了一个map,key—数据源,value----数据库连接,事务其实就是指向同一个连接的,只有拥有同一个数据库连接才能同时提交和回滚,如果在不同的线程,数据库的连接不是同一个,所以事务也不是同一个 。
private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");


推荐阅读