Spring 事物注解@Transactional解析

一、@Transactional注解简介

@Transactional是Spring框架中基于 AOP 的一个注解,用于在方法级别控制事务。这个注解告诉Spring框架在方法执行过程中,使用事务管理功能。如果该方法正常执行,则事务将被提交;如果方法抛出异常,则事务将被回滚。

二、@Transactional 工作原理

通过动态代理为标注了@Transactional注解的方法织入切面逻辑并生成对应的代理对象,而切面逻辑就是实现事务的相关操作,再通过这个代理对象去调用将目标方法以及切面中的事务处理逻辑。切面中的事务处理逻辑主要做了以下操作:

  1. 获取方法上标注的注解的元数据,包括传播行为、隔离级别、异常配置等信息。
  2. 通过ThreadLocal获取事务上下文,检查是否已经激活事务。
  3. 如果已经激活事务,则根据传播行为,看是否需要新建事务。
  4. 开启事务,先通过数据库连接池获取链接,关闭链接的autocommit,然后在try catch里反射执行真正的数据库操作,通过异常情况来决定是commit还是rollback。

下面使用伪代码简单描述下@Transactional注解标注的方法具体的执行逻辑。

	@Transactional
    public void updateUser() {
        // 执行业务逻辑
        doUpdate();
    }

    // 等价于
    public void updateUser() {
        //事务管理器-开启事务-拿到一个事务
        transactionalManager.beginTranscation;
        //关闭事务自动提交,需要手动提交
        autoCommit = false;
        public void invoke () {
            try {
                // 执行业务逻辑
                doUpdate();
            } cache(Exception e) {
                // 异常回滚
                Rollback();
            }
            //提交
            commit();
        }
    }

三、@Transactional 常用属性

  1. propagation:定义事务的传播行为。
  2. isolation:定义事务的隔离级别。
  3. rollbackFor:用于指定哪些异常需要回滚事务。
  4. noRollbackFor:用于指定哪些异常不需要回滚事务。
  5. timeout:用于设置事务的超时时间,单位为毫秒。

四、事务的传播机制

假设serviceA中有个methodA()方法中有个更新A表的操作同时还调用了serviceB中的methodB()方法,methodB()方法中有个更新B表的操作,下面分析下各种传播机制的特点。

public void methodA () {
	// 更新A表
	updateA();
	// 调用methodB方法
	methodB();
}

public void methodB () {
	// 更新B表
	updateB();
}
  1. REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。在大多数场景下,使用 REQUIRED 传播行为即可满足需求。
    (1)场景一:methodA 没有起事务,methodB 会重新创建一个事务B。当调用methodA 执行时,更新完A表后,再调用methodB方法,如果methodB执行过程中出现异常,事务B会回滚对B表的更新操作。但是由于methodA 没有起事务,A表的更新操作并不会回滚。
    (2)场景二:methodA 已经起了个事务A,methodB 不会去创建新的事务,而是加入到事务A中。这时两个方法在同一个事务A中,无论哪个方法出现异常,两个方法所做的操作都会回滚。

  2. REQUIRES_NEW:创建一个新事务,如果当前存在事务,把当前事务挂起,直到新事务完成。这种传播行为适用于需要独立于当前事务的场景。
    (1)场景一:methodA 没有起事务,methodB 会重新创建一个事务B。当调用methodA 执行时,更新完A表后,再调用methodB方法,如果methodB执行过程中出现异常,事务B会回滚对B表的更新操作。但是由于methodA 没有起事务,A表的更新操作并不会回滚。
    (2)场景二:methodA 已经起了个事务A,methodB 也会创建新的事务B。调用methodA时先执行更新A表操作,再进入methodB方法,这时事务A会挂起,转而执行事务B。如果methodB方法执行出现异常,事务B会回滚对B表的更新操作,同时异常抛到 methodA 方法,导致事务A也会回滚对A表的操作。
    (3)场景三:methodA 已经起了个事务A,methodB 也会创建新的事务B。调用methodA时先执行更新A表操作,再进入methodB方法,这时事务A会挂起,转而执行事务B。如果methodB方法执行出现异常,事务B会回滚对B表的更新操作,同时异常抛到 methodA 方法,但是异常被methodA 方法捕获了,此时methodA 能正常执行完成,所以事务A不会回滚对A表的更新操作。
    (4)场景四:methodA 已经起了个事务A,methodB 也会创建新的事务B。调用methodA时先执行更新A表操作,再进入methodB方法,这时事务A会挂起,转而执行事务B。methodB方法正常执行完成,事务B提交对B表的更新操作,然后回到 methodA 方法继续执行时methodA 出现异常,此时事务A会回滚对A表的更新操作,但是由于事务B与事务A是不同的两个事务,所以事务B的提交不会受到事务A的影响。

  3. SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,就以非事务方式执行。这种传播行为适用于不强制要求事务,但可以充分利用现有事务的场景。
    (1)场景一:methodA 没有起事务,methodB 也不会创建事务。当调用methodA 执行时,更新完A表后,再调用methodB方法,如果methodB对B表的完成更新操作后出现异常。由于methodB没有起事务,所以methodB对B表的完成更新操作不会回滚,
    methodA 对A表的更新操作也不会回滚。
    (2)场景二:methodA 已经起了个事务A,methodB 会加入到事务A中。这时两个方法在同一个事务A中,无论哪个方法出现异常,两个方法所做的操作都会回滚。

  4. NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。这种传播行为适用于不需要事务支持的场景。
    (1)场景一:methodA 没有起事务,methodB 不会创建事务。两个方法都是没有起事务的,所以出现异常前所做的更新操作都不会回滚。
    (2)场景二:methodA 已经起了个事务A,methodB 也不会创建事务。调用methodA 更新操作后出现异常,事务A会回滚对A表的更新操作,methodB 更新操作后出现异常,对B表的更新操作不会回滚。

  5. MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。这种传播行为适用于必须在事务中执行的场景。
    (1)场景一:methodA 没有起事务,调用 methodB 直接回报错。由于 methodA 没有事务,对A表的更新操作不会回滚,调用methodB 会报错,不会执行更新B表的操作。
    (2)场景二:methodA 已经起了个事务A,methodB 会加入到事务A中。这时两个方法在同一个事务A中,无论哪个方法出现异常,两个方法所做的操作都会回滚。

  6. NEVER:必须以非事务方式执行,如果当前存在事务,则抛出异常。这种传播行为适用于不能在事务中执行的场景。
    (1)场景一:methodA 没有起事务,methodB 也不会创建事务。由于 methodA 和 methodB 都没有开启事务,两者的操作都不会回滚。
    (2)场景二:methodA 已经起了个事务A,调用 methodB 直接回报错。事务A 更新完A表后,调用methodB 会报错,事务A 就会回滚对A表的操作,methodB 不会执行。

  7. NESTED:如果当前存在事务,则以嵌套事务的方式执行;如果不存在事务,则创建一个新的事务。嵌套事务是一种特殊的事务,它可以独立于外部事务进行回滚,但提交时仍依赖于外部事务,并且。这种传播行为适用于需要在单个事务内执行多个独立操作的场景。
    (1)场景一:methodA 没有起事务,methodB 会创建一个新事务B。methodB 抛异常后会回滚对B表的操作;methodA没有事务,对A表的操作不会回滚。
    (2)场景二:methodA 已经起了个事务A,methodB 会创建一个事务A的子事务A_B。当methodA方法中抛异常后,methodA 和methodB 的更新操作都会回滚。事务A_B是事务A的一个子事务,如果事务A回滚,那么子事务A_B也必须回滚,这也是与新事务B不同的地方,新事务B与事务A属于两个不同的事务,事务A的回滚不会影响事务B。
    (2)场景二:methodA 已经起了个事务A,methodB 会创建一个事务A的子事务A_B。当methodB 方法中抛异常后,methodA 中捕获了methodB 的异常,则只要子事务A_B会回滚,事务A不会进行回滚。

五、事务的隔离级别

  1. ISOLATION_DEFAULT:使用底层数据库的默认隔离级别。是spring默认设置。
  2. ISOLATION_READ_UNCOMMITTED:可以读取其他事务未提交的数据。这是最低的隔离级别,可能会导致脏数据问题。
  3. ISOLATION_READ_COMMITTED:只能读取其他事务已提交的数据,可以解决脏读;但也能读取到在当前事务开始之后提交的数据,可能会出现不可重复读的问题。
  4. ISOLATION_REPEATABLE_READ:可以重复读取当前事务开始之前其他事务已提交的数据,之后其他事务提交的数据读取不到,可以避免脏读以及不可重复读的问题,但是可能会出现幻读的问题。
  5. ISOLATION_SERIALIZABLE:完全序列化。这是最高的隔离级别,能确保在同一时刻,只能有一个事务访问数据库。

你可能感兴趣的:(spring,spring,数据库,java)