目录
4、springboot中的事务的隔离性 mysql 的事务隔离级别:Repeatable Read
1、事务介绍
1.1事务
应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。
1.2 事务的特征ACID
(1)原子性Atomicity:一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
(2)一致性Consistency:事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
(3)隔离性Isolation:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
(4)持久性Durability:持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
2、springboot事务流程
1.1 当@Transactional注解的方法被类外部的代码调用时,Spring在运行时为方法所在类生成一个AOP代理对象。 代理对象根据@Transactional的属性,决定是否由事务拦截器TransactionInterceptor对此方法进行事务拦截。在进行事务拦截时,会先开启事务,然后执行业务代码,根据执行是否出现异常,通过抽象事务管理器AbstractPlatformTransactionManager来进行rollback或者commit。
1.2 在进行方法调用的时候,发现这个方法有事务注解,AOP首先会检测到,然后用代理类采用反射机制进行调用。
(1)首先调用了CglibAopProxy.intercept()方法。
(2)接下来调用ReflectiveMethodInvocation.proceed()方法,
(3)TransactionInterceptor.invoke()
(4)TransactionAspectSupport.invokeWithinTransaction()
(5)TransactionAspectSupport.createTransactionIfNecessary()
(6) AbstractPlatformTransactionManager.getTransaction(),创建了一个新的事务。
1.3事务回滚时机:任何的RuntimeExcetipn、Error将触发回滚,任何的checkedException不触发回滚
3、springboot中的事务的传播特性
3.1 Propagation.REQUIRED(默认属性):如果当前没有事务 ,则自己新建一个事务 ,子方法是必须运行在这个事务中的 ; 如果当前存在事务 , 则加入这个事务 , 成为一个整体。一般用于增删改。
A、使用条件:暂时未知
B、回滚策略:
① 内/外只要有报错,他俩会一起回滚。
② 只要内层方法报错抛出异常,即使外层有try-catch,该事务也会回滚。
理解:1、外层事务先提交
2、事务1(REQUIRED),事务2(REQUIRED),事务3(REQUIRED);事务3调用事务1和事务2。那么事务1事务2事务3其实就是一个事务,只要有一个出现异常则都会回滚。
3.2 Propagation.SUPPORTS: 如果当前有事务 , 则使用事务 ,如果当前没有事务 ,则不使用事务。适用于查询
A、使用条件:暂时未知
B、回滚策略:同REQUIRED
① 外层事务正常,内层报错,即使外层有无try-catc→ 内外层都回滚
② 外层事务异常,内层正错 → 内外层都回滚。
③ 外层无事务正常,内层报错→外层正常提交。
3.3 Propagation.MANDATORY : 该传播属性强制必须存在一个事务,加入当前事务 ,如果不存在,则抛出异常;
A、使用条件:必须是事务方法调用此方法
B、回滚策略:同REQUIRED
3.4 Propagation.NEVER : 以非事务方式执行,如果当前有事务存在 ,则抛出IllegalTransactionStateException异常;
A、使用条件:外层无事务
B、回滚步骤:
① 外层事务正常,内层有无报错→内外层都回滚。
② 内外层无事务,都提交。
3.5 Propagation.NOT_SUPPORTED :以非事务方式执行,如果当前有事务 ,则把事务挂起 ;
A、使用条件:未知
B、回滚步骤:
① 外层事务正常,内层报错→外层回滚,内层提交
② 外层事务正常,内层报错,且try-catch →内外层都提交
3.6 Propagation.REQUIRES_NEW : 如果当前有事务 ,则挂起事务 ,并且自己创建一个新的事务给自己使用; 如果当前没有事务 , 则同required一样 。这种独立的内部事务还可以声明其自己的隔离级别、超时和仅读设置,并且不继承外部事务的特征。注意:创建的新的事务先提交
A、使用条件:暂时未知
B、回滚步骤:
① 内层报错回滚,外层也回滚。外层try-catch内层的异常,外层不会回滚。
② 外层报错回滚,不影响内层。
3.7 Propagation.NESTED : 如果当前有事务,则开启子事务(嵌套事务), 嵌套事务是独立提交或者回滚的 。
A:使用条件,
① JDK版本要在1.4以上,有java.sql.Savepoint。因为nested就是用savepoint来实现的。
② 事务管理器的nestedTransactionAllowed属性为true。
③ JPA、Hibernate不支持嵌套事务,jdbc3.0以上支持嵌套事务。mybatis测试支持嵌套事务
B、回滚步骤:
① 外层异常,内外都会回滚
② 内层异常,内外都会回滚。try-catch 子事务,内存回滚,外层不回滚
理解:1、如何使同一个事务中的两个子事务,是不同事务?可以使事务互不影响 REQUIRES_NEW、NESTED
2、 当同一个类中 事务1(REQUIRED) 调用 事务2(NEVER),不会报错?但是事务2、1都不会执行
当事务1(REQUIRED)是在类A,事务2(NEVER)是在类B,事务1调用事务2是会报错,且事务1、2都会会回滚
4、springboot中的事务的隔离性 mysql 的事务隔离级别:Repeatable Read
Isolation.DEFAULT(-1): 默认值,表示使用底层数据库的默认隔离级别。大部分数据库为READ_COMMITTED
Isolation.READ_UNCOMMITTED(1): 未提交读(READ_UNCOMMITTED)是最低的隔离级别,其含义是允许一个事物读取另一个事物没提交的数据。优点在于并发能力高,适合那些对数据一致性没有要求而追求高并发的场景,最大缺点是出
现脏读。
Isolation.READ_COMMITTED(2): 读写提交(READ_COMMITTED),一个事务只能读取另外一个事务已提交的数据,不能读取未提交的数据。该级别克服了脏读,但不可重复读。
Isolation.REPEATABLE_READ(4):可重复读(REPEATABLE_READ),目标是克服读写提交中出现的不可重复读的现象,但会出现幻读。(幻读:两次查到的数据不一样)
Isolation.SERIALIZABLE(8):串行化(SERIALIZABLE),是数据库最高的隔离级别,它能够完全保证数据的一致性,但性能降低了。
◆ 脏读(dirty read):当一个事务读取另一个事务尚未提交的修改时,产生脏读。
◆ 不可重复读(non-repeatable read):同一查询在同一事务中多次进行,由于其他提交事务所做的修改或删除,每次返回不同的结果集,此时发生非重复读。:
◆ 幻像读(phantom read):同一查询在同一事务中多次进行,由于其他提交事务所做的插入操作,每次返回不同的结果集,此时发生幻像读。
对于查询:
隔离界别 | 脏读 | 不可重复读 | 幻读 |
Read_uncommitted读写未提交的 | √ | √ | √ |
Read_committed 读写已提交的 | × | √ | √ |
Repeatable_read 可重复读 | × | × | √ |
serializable | × | × | × |
5、readOnly属性 (默认false)
readOnly=true表明所注解的方法或类只是读取数据。 readOnly=false表明所注解的方法或类是增加,删除,修改数据。
理解:当前事务中如果出现了增删改语句,那么抛出异常TransientDataAccessResourceException。
6、其他属性
① rollbackFor属性
Spring框架的事务管理默认地只在发生非受检异常(RuntimeException和Error)时才进行事务回滚。也就是说,当事务方法抛出受检异常(Exception中除了RuntimeException及其子类以外的)时不会进行事务回滚。怎么解决?
@Transactional(rollbackFor=Exception.class)就可以实现,当发生受检异常(checked exceptions)时,事务也进行回滚。
②RollbackForClassName官方解释:派生自Throwable的类名数组。你必须引起回滚的异常类名称的可选数组。
这个就不用像上面的异常一样把所有的受检异常全部回滚,由你自己决定哪些需要回滚。
③noRollbackFor官方解释:不能回滚的异常类数组
④NoRollbackForClassName ,与上面县相反
⑤ value/transactionManager:可选的限定词,指定要使用的事务管理器。
7、在SpringBoot2.0中使用使用需要注意的地方。
3.1 加@Transactional的方法不能是private和protected修饰,private会直接报编译错误,protected不会报错。但是事务不起作用。
3.2 @Transactional可以放在Controller下面直接起作用。
3.3 @Transactional引入包问题,她有两个包:import javax.transaction.Transactional; 和 import org.springframework.transaction.annotation.Transactional; 这两个都可以用,对比了一下他们两个的方法和属性,发现后面的比前面的强大。建议后后面的。
3.4 @Transactional 可以注解在类上面也可以注解在方法上面。
以下为自己的理解----
同类中:@Transactional 用法没有其他参数
1、一个非事务方法调用另一个事务方法方法,不生效;
spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所以就不会启动transaction,我们看到的现象就是@Transactional注解无效。
可以让他生效,方法一:放在另一个类中去,方法二:获取代理对象然后,调用这个方法。
2、一个事务方法掉调用另一个非事务方法,生效;
出现异常,同时回滚,不管try-catch与否。
3、一个事务方法调用另一个事务方法,生效;
回滚步骤:两个方法应该是同一个事务,同时回滚。
4、同一个类中,事务方法使用普通方法调用注解了事务【NESTED或者REQUIRED_NEW】方法他不会有对应的效果。只会把它当成普通方法作为这个事务的一部分同时回滚。要想生效可以获取他的代理对象然后调用。
5、使用场景:【不同类中或者同一类中使用代理对象 调用】
①当事务1调用事务2时,我需要事务1的执行成功与否不影响事务2执行,但是事务2的成功与否影响事务1的执行?
把事务2的传播属性设置为REQUIRED_NEW,最先执行事务2。
② 当事务1调用事务2时,内层事务异常,内层回滚,外层不回滚?
把事务2的传播属性设置为NESTED,且try-catch内层的调用。
补充:获取代理对象的方法
@Service
@Slf4j
@EnableAspectJAutoProxy(exposeProxy = true)
public class QuizServiceImpl extends AbstractShop implements IQuizService {
@Resource
private QuizLogMapper quizLogMapper;
@Resource
private ShopManageService shopManageService;
@Override
@Transactional(rollbackFor = Exception.class)
public void testA(){
QuizPO po = new QuizPO();
po.setSubject("测试主题一");
quizMapper.insert(po);
System.out.println("看看数据库是否有测试主体一");
// 开启子事务
try{
IQuizService iQuizService = (IQuizService) AopContext.currentProxy();
iQuizService.testB();
}catch (Exception e){
e.printStackTrace();
}
}
@Override
@Transactional(propagation = Propagation.MANDATORY )
public void testB(){
QuizPO po = new QuizPO();
po.setSubject("测试主题二");
quizMapper.insert(po);
throw new IllegalArgumentException("我抛出了异常!看看数据库还有没有的数据!");
}
}
参考:
1、官方 Data Access (spring.io) 页面内搜索1.4.7 Transaction Propagation 了解事务的传播
2、(2条消息) Spring 注解@Transactional readOnly=true_方逸涛的专栏-CSDN博客