6.Spring事务
(1)Spring事务管理的方式有几种?
1.编程式事务:在代码中硬编码,通过 TransactionTemplate或者TransactionManager手动管理事务(不推荐使用)。
2.声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务(实际是通过 AOP 实现(基于@Transactional 的全注解方式使用最多))。
(2)Spring 事务管理接口介绍
PlatformTransactionManager:(平台)事务管理器,Spring事务策略的核心。
TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。
TransactionStatus:事务运行状态。
(3)Spring 事务中有哪几种事务传播行为?
事务传播行为是为了解决业务层方法之间互相调用的事务问题。
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
正确的事务传播行为可能的值如下:
1.PROPAGATION_REQUIRED
使用的最多的一个事务传播行为,如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。我们平时经常使用的@Transactional注解默认使用就是这个事务传播行为。
2.PROPAGATION_REQUIRES_NEW
创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
3.PROPAGATION_NESTED
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
4.PROPAGATION_MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)这个使用的很少。
若是错误的配置以下 3 种事务传播行为,事务将不会发生回滚:
1.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
2.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
3.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
例如,一个方法A内部调用了另一个方法B,如果方法B具有REQUIRED(默认)的事务传播行为,而方法A已经在一个事务中执行,那么方法B将加入到方法A的事务中,共同参与事务的操作。
REQUIRED:
两个表没有变化,说明所有的方法都是回滚了的。这就体现了REQUIRED这个传播行为,一个方法回滚了,其它所有方法都回滚。
更具体的:LogService中的add方法本身有事务,UserService中的add方法也是REQUIRED。这时候,LogService中的add就加入了UserService中的事务,相当于一个整体。
UserService
中add
方法的事务没有回滚,LogService
中的事务回滚了,回忆REQUIRES_NEW
:每次都会创建一个新的事务,如果当前存在事务,则将当前事务挂起。在这里,LogService
中的事务先执行,执行完后再执行UserService
中的事务。NESTED(嵌套事务)演示:
LogService
中的事务已经回滚,但是嵌套事务不会回滚嵌套之前的事务,也就是说嵌套事务可以实现部分事务回滚。
NESTED(嵌套事务)与 REQUIRES_NEW的区别:
NESTED(嵌套事务):
在嵌套事务中,内部事务实际上是由外部事务开启和提交/回滚的。 当外部事务回滚时,会导致内部事务也被回滚,即使内部事务已经执行了一些提交操作。
这是因为嵌套事务的模拟通过保存和恢复事务状态来实现,当外部事务回滚时,它会回滚到开启内部事务的那个点,包括内部事务执行的任何修改或提交。这样可以确保事务的一致性。
REQUIRES_NEW:
REQUIRES_NEW表示创建一个独立的事务。当一个事务(外部事务)调用另一个带有REQUIRES_NEW传播行为的事务时,内部事务将在一个新的事务中执行,独立于外部事务。内部事务的提交或回滚不会影响外部事务。无论外部事务是否回滚,内部事务都可以独立提交或回滚。
(4)Spring事务中的隔离级别有哪几种?5
在TransactionDefinition接口中定义了五个表示隔离级别的常量:
1.ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。
2.ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
3.ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
4.ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
5.ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
和事务传播行为这块一样,为了方便使用,Spring 也相应地定义了一个枚举类:Isolation
public enum Isolation { DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED), READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED), REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ), SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE); private final int value; Isolation(int value) { this.value = value; } public int value() { return this.value; }
}
当使用 @Transactional 注解修饰方法时,它只对public的方法生效。
当使用 @Transactional 注解修饰类时,表示对该类中所有的public方法生效。
这是因为基于代理的事务管理机制在运行时创建代理对象,并且代理对象只能访问public方法。当@Transactional注解应用于非public方法(如protected、private或默认包可见性的方法)时,代理对象无法访问这些方法,导致事务管理无法生效。
# @Transactional(rollbackFor = Exception.class)注解了解吗?
Exception
分为运行时异常 RuntimeException
和非运行时异常。事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。
当 @Transactional
注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。
在 @Transactional
注解中如果不配置rollbackFor
属性,那么事务只会在遇到RuntimeException
的时候才会回滚,加上 rollbackFor=Exception.class
,可以让事务在遇到非运行时异常时也回滚。
处理了异常后,事务没有回滚,这样的操作非常的危险,但是也有解决的方法,那就是手动回滚事务:
@Transactional public Integer delete(Integer id){ ... try { int x = 8 / 0; }catch (Exception e){ System.out.println(e.getMessage()); //手动回滚事务 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } }