首页 > 编程知识 正文

spring事务传播机制,spring事务注解失效场景

时间:2023-05-03 06:50:18 阅读:242656 作者:1569

Spring事务小结 Spring事务传播机制Spring事务失效场景Spring 事务四大特性事务隔离级别产生的问题Spring 手动开启事务

Spring事务传播机制 Spring 七种传播机制REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED)支持当前事务,如果有事务加入当前事务,没有则会创建一个新的事务SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS)支持当前事务,如果有事务加入当前事务,如果没有事务的话以非事务方式执行MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY)支持当前事务,当前如果没有事务抛出异常REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW)不管是否有事务创建一个新的事务并挂起当前事务NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED)以非事务方式执行,如果当前存在事务则将当前事务挂起NEVER(TransactionDefinition.PROPAGATION_NEVER)以非事务方式进行,如果存在事务则抛出异常NESTED(TransactionDefinition.PROPAGATION_NESTED)如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

注意:事务的传播在类内部方法中调用时是失效的

以PROPAGATION_REQUIRED为例子讲一下传播机制中的当前事务和新建事务的概念。

假设有以下几个类

ContollerAServiceA,save方法被@Transactional(propagation = Propagation.REQUIRED)修饰ServiceB,delete方法被@Transactional(propagation = Propagation.REQUIRED)修饰方法调用链为ControllerA -> ServiceA#save() -> ServiceB#delete()

那么时序图如下图1-1所示。

图1-1 解析如下

对于ServiceA的save方法来说,由于Controller调用的时候没有开启任何事务,因此,Spring内部识别到REQUIRED传播机制则会主动开启一个事务。

对于ServiceB的delete方法来说,由于save方法已经开启了一个事务,那么对于delete方法来说当前已经存在了一个事务,那么Spring就不会再为delete方法开启一个事务,而是直接把delete方法加入save方法前开启的事务中。

最后save方法和delete方法执行完之后,Spring则会内部把事务提交上去。

Spring事务失效场景

数据库引擎不支持事务,比如MyISAM

入口方法不是public,这一点由Spring的AOP特性决定的,理论上而言,不public也能切入,但spring可能是觉得private自己用的方法,应该自己控制,不应该用事务切进去吧)。另外private 方法, final 方法 和 static 方法不能添加事务,加了也不生效

Spring事务管理默认只支持运行期异常进行回滚(至于为什么spring要这么设计:因为spring认为Checked的异常属于业务的,coder需要给出解决方案而不应该直接扔该框架)

没有启用事务和切面

类是否被正确代理

业务和事务要在同一个线程,否则事务也不生效,比如

@Transactional@Overridepublic void save(User user1, User user2) { new Thread(() -> { saveError(user1, user2); System.out.println(1 / 0); }).start();}

默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

Spring 事务四大特性 原子性 : 操作要么全部成功,要么全部失败回滚一致性 : 事务执行前和执行后处于一致性状态。例如,转账前A、B共5000元,A、B之间转账后,两者之和仍应该是5000元。隔离性 : 事务之间互不干扰持久性 : 事务一旦提交,数据的改变是永久性的,即使这时候数据库发生故障,数据也不会丢失。 事务隔离级别

未提交读(READ UNCOMMITED)

在未提交读级别下,事务中的修改,即使没有提交,对其他事务也是可见的,事务可以读取未提交的数据,这也被称为脏读

提交读(READ COMMIT)

一个事务开始时,只能看见已经提交的事务所做的修改,换句话说,一个事务直到提交之前,所做的任何修改对其他事务都是不可见的,这个级别有时候也被称为不可重复读,因为两次执行同样的查询可能会得到不同的结果

可重复读 (REPEATABLE READ)

可重复读解决了脏读的问题,该级别保证了统一个事务中多次读取同样记录的结果是一致的。但是理论上,可重复读隔离级别无法解决幻读问题,所谓幻读,指的是当某个事务在读取某个范围内的记录时,会产生幻行,很多DB通过MVCC机制解决这个问题

序列化 (SERIALIZABLE)

序列化级别时最高的隔离级别,它通过强制事务串行执行,避免了前面所有的所有问题

产生的问题 脏读,在未提交读隔离级别出现不可重复读,在提交读隔离级别出现幻读,可重复读隔离级别通过MVCC解决 Spring 手动开启事务

使用场景,外部类需要调用父类的方法,然后父类会调用子类实现的抽象方法,父类不需要事务,但是子类实现的方法需要事务,因为外部类是调用父类的方法,在子类添加@Transactional注解无法生效,例如

@Scheduled(fixedRate = 1000 * 60 * 3) public void importPaidOrders() { // 外部调用 paidOrdersImporter.importOrders(tmb,now, TypeTkStatus.RELATION_PAID); } // 父类方法 public void importOrders(String startTime,String endTime, TypeTkStatus tkStatus){ doDealWithOrder(...); } // 抽象方法 abstract void doDealWithOrder( Orders orders, Orders previousOrder, Long parentUserId, Long parentPUserId, User user, User oneLevelUser ); // 子类实现 public void doDealWithOrder( Orders orders, Orders previousOrder, Long parentUserId, Long parentPUserId, User user, User oneLevelUser ){ ... }

这个时候可以手动开启事务,如何手动开启?

// 注入DataSourceTransactionManager @Autowired DataSourceTransactionManager dataSourceTransactionManager; // 注入TransactionDefinition @Autowired TransactionDefinition transactionDefinition; @Override @Transactional void doDealWithOrder(Orders orders, Orders previousOrder, Long parentUserId, Long parentPUserId, User user, User oneLevelUser) { // 开启事务 TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); try { // business logic ... // 提交事务 dataSourceTransactionManager.commit(transactionStatus); }catch (Exception ex){ logger.error("paid orders exception is : {}",ex); // 回滚事务 dataSourceTransactionManager.rollback(transactionStatus); } }

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。