Loading

Spring :事务传播行为

一.相关定义

事务传播行为(propagation):当前方法的事务如何传播下去(里面的方法如果用事务,是否与它公用一个事务)

spring中有其中有7种事务传播行为,默认是Reqiured级别。

public enum Propagation {

	/**
	 * Support a current transaction, create a new one if none exists.
        如果以前有事务,就用以前的事务,如果没有,就创建一个新的事务。
	 */
	REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

	/**
	 * Support a current transaction, execute non-transactionally if none exists.
      支持新的事务,如果没有事务,也可以执行
	 */
	SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

	/**
	 * Support a current transaction, throw an exception if none exists.
           当前必须存在事务,否则抛异常
	 */
	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

	/**
	 * Create a new transaction, and suspend the current transaction if one exists.
      创建一个新的事务,并且挂起以前旧的事务
	 */
	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

	/**
	 * Execute non-transactionally, suspend the current transaction if one exists.
           不支持当前存在事务,如果有就挂起当前事务
	 */
	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

	/**
	 * Execute non-transactionally, throw an exception if a transaction exists.
      不支持当前存在事务,如果有就抛异常。
	 */
	NEVER(TransactionDefinition.PROPAGATION_NEVER),

	/**
	 * Execute within a nested transaction if a current transaction exists,
     
     开启一个子事务,mysql不支持,只有支持还原点功能的数据库才行(oracle) */ NESTED(TransactionDefinition.PROPAGATION_NESTED); private final int value; Propagation(int value) { this.value = value; } public int value() { return this.value; } }

二.相关场景(常用的只有required和requires_new)

下面针对这两个构建相关场景:

外事务 {

  A()REQUIRED

  B()REQUIRES_NEW

  C()REQUIRED

  D()REQUIRES_NEW

  //外----

}

场景1:A方法出现异常,由于异常机制,导致代码停止,数据库没有任何新的数据进入

场景2:C方法出现异常,A回滚,B成功,C回滚,D和外无法执行

场景3:外执行成功后,又抛出异常。外事务感知到异常,A,C,外回滚,B,D成功。

场景4:D异常,A,C回滚,外执行不了,B成功,D自己回滚

场景5:C用try-catch执行,C出现异常回滚,外界没有感知到异常,A,B,D,外都成功。

总结:传播行为过程中,只要requires_new被执行过,就一定成功,required感知到异常就一定会回滚。

  (运行的异常默认是一定回滚的,编译时的异常默认时不会滚的,但是可以用rollbackFor去指定哪些异常一定回滚,noRollbackFor去指定哪些异常不回滚)

传播行为总是来定义:当一个事务存在时,它内部的事务该怎样执行。

三.Spring事务失效的一种场景

看下面一处典型代码

@Service
public class TestService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void a(){
        b();
        c();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void b(){

    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void c(){

    }
}

本来的想法是,b和c自己执行自己,互不干扰,谁抛异常,谁就回滚自己,按事务的传播行为来说,应该是这样,没毛病。

但是经过测试,你会发现,c如果有异常,那么执行成功的b仍然会回滚,为什么?

当我们用Controller去调用testService时,发现testService是一个Cglib代理类。

 

 

原因:spring只有被动态代理时才会产生事务,而我们在类的内部,直接调用本类的方法,是不需要经过代理对象的,调用的是TestService本类。

所以我们只需要由TestService的代理对象去执行相关方法。

1.导入相关starter

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.在springboot主应用上开启EnableAspectJAutoProxy注解,并申明可暴露代理对象

 

 3.在对应的方法中获取代理对象,并使用代理对象去执行方法

@Service
public class TestService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void a(){
        TestService proxy = (TestService)AopContext.currentProxy();
        proxy.b();
        proxy.c();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void b(){

    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void c(){

    }
}

 

posted @ 2020-09-02 22:26  秋风飒飒吹  阅读(236)  评论(0编辑  收藏  举报