Spring事务

171 阅读4分钟

Spring管理事务的方式

  • 编程试事务,在代码中硬编码(不推荐使用)
  • 声明式事务,在配置文件中配置(推荐使用)
    • 基于XML的声明式事务
    • 基于注解的声明式事务

数据不一致问题

在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性问题

  • 脏读

指脏数据指在不同的事务下,当前事务可以读到另外事务未提交的数据。例如:T1修改一个数据但未提交,T2随后读取这个数据。如果T1撤销了这次修改,那么T2读取的数据是脏数据

  • 不可重复读

指在一个事务内多次读取同一数据集合。在这一事务还未结束前,另一事务也访问了该同一数据集合并做了修改,由于第二个事务的修改,第一次事务的两次读取的数据可能不一致。

  • 幻影读

幻影读的本质是不可重复读,T1读取某个范围内的数据,T2在这个范围内插入新的数据,T1再次读取这个范围的数据,此时读取的结果和第一次读取的结果不一致

Spring的事务隔离级别

TransactionDefinition 定义了五个隔离级别

  • ISOLATION_DEFAULT:采用后端数据库默认隔离级别,MySQL默认是可重复读(repeatable read),Oracle是提交读(read committed)
  • ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据,会导致脏读,不可重复读,幻影读
  • ISOLATION_READ_COMMITTED:允许事务读取并发已提交的数据,可以阻止脏读,但不能阻止不可重复读,幻影读
  • ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,可以阻止脏读,不可重复读,幻影读
  • ISOLATION_SERIALIZABLE:最高的隔离级别,所有的事务逐个执行,可以阻止脏读,不可重复读,幻影读。但会影响性能。

事务传播行为

事务传播行为是为了解决业务层方法之间互相调用的事务问题,当事务方法被另一个事务方法调用的,必须指定事务如何传播

  • 支持当前事务的情况
    • PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
    • PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方法继续运行
    • PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
  • 不支持当前事务的情况
    • PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起
    • PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起
    • PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常
  • 其他情况
    • PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED
  • Demo

A类的methodA方法调用了B类的methodB方法,如果methodB方法发生异常回滚,如何配置事务传播行为才能让B类的Method回滚。

public class A {
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void methodA() {
        B b = new B();
        b.methodB();
    }
}

public class B {
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void methodB() {
    }
}
  • TransactionDefinition.PROPAGATION_REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个事务

@Transactional(rollbackFor = Exception.class)注解

我们知道:Exception分为运行时异常RuntimeException和非运行时异常(IOException、SQLException)。事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。

当@Transactional注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。

在@Transactional注解中如果不配置rollbackFor属性,那么事务只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事务在遇到非运行时异常时也回滚。