Spring事务Transactional详情

Spring 事务传播机制(生命周期)

note:以下举例中 methodA 属于外部方法,和 methodB , methodC , methodD 不在同一个类中。

1 required

required:如果当前所处外围方法已有事务,则加入到外围方法的事务中。若外围方法没有声明事务,则新开启一个事务执行。

	/**
	 * Support a current transaction, create a new one if none exists.
	 */
  • 外部方法未声明事务,内部方法间不受影响
public void methodA() throws Exception {
    methodB();
    methodC();
    throw new Exception("exception in method a");
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
    TestEntity test = new TestEntity("required-b", "25");
    TestEntity result = testRepository.save(test);
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodC() {
    TestEntity test = new TestEntity("required-c", "25");
    TestEntity result = testRepository.save(test);
}
// B和C方法均可以正常插入
//methodA方法不处于事务内,所有在methodA中抛出异常不影响其他两个独立的事务
public void methodA() throws Exception {
    methodB();
    methodC();
    throw new Exception("exception in method a");
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
    TestEntity test = new TestEntity("required-b", "25");
    TestEntity result = testRepository.save(test);
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodC() throws Exception {
    TestEntity test = new TestEntity("required-c", "25");
    TestEntity result = testRepository.save(test);
    throw new Exception("exception in method c");
}	
// B插入成功,C插入失败;二者都在自己的事务里执行,methodC不影响methodB的执行结果。

  • 外部方法声明事务,所有的方法都会加入到同一个事物中执行
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA(){
    methodB();
    try {
        methodC();
    } catch (Exception e) {
        System.out.println("catch exception c");
    }
}

public void methodB() {
    TestEntity test = new TestEntity("required-b", "25");
    TestEntity result = testRepository.save(test);
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodC() throws Exception {
    TestEntity test = new TestEntity("required-c", "25");
    TestEntity result = testRepository.save(test);
    throw new Exception("exception in method c");
}	
//B,C均插入失败;A,B,C属于同一个事物
//当外围方法methodA添加上事务声明时,mehtodB和methodC会加入到外围方法methodA的事务中执行。此时无论methodA,methodB,methodC哪一个方法抛出异常都会导致所有方法的回滚。
  • 当外部方法A声明了事务,即使被调用方法B抛出的异常在A中被捕获,也会导致事务回滚。因为REQUIRED修饰的方法C和A属于同一个事务。
  • 当外部方法A声明了事务,在A中产生的异常在A中被捕获,不会导致事务回滚。因为此时异常并未被事务感知。

2 requires_new

requires_new 声明一个新的事务,和其他事务独立执行;即使外部方法已经存在事务,会开启一个新的的事务,两个事务互不影响。

	/**
	 * Create a new transaction, and suspend the current transaction if one exists.
	 */
  • 外部方法未开启事务,被调用方法事务传播机制为requires_new

此时各个方法运行在自己独立的事务中,互不影响。

  • 外部方法开启事务
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
    methodB();
    mehtodC();
    methodD();
    throw new Exception();
}

public void methodB() {
    TestEntity test = new TestEntity("required-b", "25");
    TestEntity result = testRepository.save(test);
}

@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodC() {
    TestEntity test = new TestEntity("required-c", "25");
    TestEntity result = testRepository.save(test);
}	

@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodD() throws Exception {
    TestEntity test = new TestEntity("required-d", "25");
    TestEntity result = testRepository.save(test);
    throw new Exception("exception in method d");
}	
// methodB失败,methodC成功,methodD失败
// 其中B加入到了A的事务中,随着A的事务回滚,B也跟着回滚。C开启一个新的事务,不受其他事务的影响。D开启新事务。
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
    methodB();
    mehtodC();
    methodD();
}

public void methodB() {
    TestEntity test = new TestEntity("required-b", "25");
    TestEntity result = testRepository.save(test);
}

@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodC() {
    TestEntity test = new TestEntity("required-c", "25");
    TestEntity result = testRepository.save(test);
}	

@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodD() throws Exception {
    TestEntity test = new TestEntity("required-d", "25");
    TestEntity result = testRepository.save(test);
    throw new Exception("exception in method d");
}	
// B失败,C成功,D失败
// 由于C开启新的事务,不受其他事务的影响。D开启新的事务,D中抛出异常回滚。A受到D抛出异常的影响回滚,由于B加入A的事务,所以B一同回滚。

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
    methodB();
    mehtodC();
    try{
        methodD(); 
    }catch(Exception e){
        System.out.print(e.getMessage());
    }
}
//B成功,C成功,D失败
//此时A和D处于不同的事务中,且D的异常被捕获,A和B不受影响。

3 nested

嵌入事务:当外部方法没有声明事务时,nested和required一致;当外部方法声明了事务时,声明了nested事务类型的子方法会以子事务的方式嵌入到外部事务,父级事务影响子事务,如果子事务抛出异常不被父事务感知,则父级事务不受影响;子事务可以单独回滚,并级的子事务间不会相互影响。

	/**
	 * Execute within a nested transaction if a current transaction exists,
	 */
  • 外部方法未声明事务
public void methodA() throws Exception{
    methodB();
    mehtodC();
    methodD();
    throw new Exception();
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
    TestEntity test = new TestEntity("required-b", "25");
    TestEntity result = testRepository.save(test);
}

@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodC() {
    TestEntity test = new TestEntity("required-c", "25");
    TestEntity result = testRepository.save(test);
}	

@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodD() throws Exception {
    TestEntity test = new TestEntity("required-d", "25");
    TestEntity result = testRepository.save(test);
    throw new Exception("exception in method d");
}	
// B成功,C成功,D失败
//外部方法未开启事务,NESTED类型的事务会以独立事务的方式执行(类似REQUIRED),互不影响。
  • 外部方法声明事务
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
    TestEntity test = new TestEntity("required-b", "25");
    TestEntity result = testRepository.save(test);
}

@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodC() {
    TestEntity test = new TestEntity("required-c", "25");
    TestEntity result = testRepository.save(test);
}	

@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodD() throws Exception {
    TestEntity test = new TestEntity("required-d", "25");
    TestEntity result = testRepository.save(test);
    throw new Exception("exception in method d");
}	

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
    methodB();
    mehtodC();
    methodD();
}
// 全部失败
//B加入到A的事务中,C和D以子事务的方式嵌入到A事务。D抛出异常导致A-B事务回滚,同样子事务C也会跟随父级事务回滚。
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
    TestEntity test = new TestEntity("required-b", "25");
    TestEntity result = testRepository.save(test);
}

@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodC() {
    TestEntity test = new TestEntity("required-c", "25");
    TestEntity result = testRepository.save(test);
}	

@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodD() throws Exception {
    TestEntity test = new TestEntity("required-d", "25");
    TestEntity result = testRepository.save(test);
    throw new Exception("exception in method d");
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
    methodB();
    try{
        methodD();    
    }catch(Exception e){
        System.out.println(e.getMessage());
    }
     mehtodC();
}
// B,C成功,D失败
// D子事务异常被捕获,A-B父事务无感知,C子事务不受影响

requirednested的区别在于:如果存在外部事务,required是加入已经存在的事务,和外部事物属于同一个事务。而nested是以子事务的方式嵌入到外部事务,只要子事务的异常不被外部事务感知,就不会影响外部事物。

requires_newnested的区别在于:二者相同的地方在于,内部(子)事务异常不被外部事物感知时不影响外部事物;不同点在于,外部事物的异常不会影响内部事务。而外部事物的异常一定会影响nested子事务,导致nested子事务同样回滚。

4 supports

supports:如果当前存在外部事务,则加入该外部事务;如果不存在外部事务,则以非事务模式执行。

	/**
	 * Support a current transaction, execute non-transactionally if none exists.
	 */
  • 外部事物不存在
public void methodA() throws Exception{
    methodB();
    mehtodC();
    methodD();    
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
    TestEntity test = new TestEntity("test-b", "25");
    TestEntity result = testRepository.save(test);
}

@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodC() {
    TestEntity test = new TestEntity("test-c", "25");
    TestEntity result = testRepository.save(test);
}	

@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodD() throws Exception {
    TestEntity test = new TestEntity("test-d", "25");
    TestEntity result = testRepository.save(test);
    throw new Exception("exception in method d");
}
// B,C,D均成功
// B在自己事务里执行,C,D以非事务方式执行
  • 存在外部事物
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
    methodB();
    mehtodC();
    try{
        methodD();    
    }catch(Exception e){...}
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
    TestEntity test = new TestEntity("test-b", "25");
    TestEntity result = testRepository.save(test);
}

@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodC() {
    TestEntity test = new TestEntity("test-c", "25");
    TestEntity result = testRepository.save(test);
}	

@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodD() throws Exception {
    TestEntity test = new TestEntity("test-d", "25");
    TestEntity result = testRepository.save(test);
    throw new Exception("exception in method d");
}
// B,C,D均失败
// 存在外部事物,required和support均会加入到外部事务,A-B-C-D属于同一个事务。

requiredsupports的区别在于:当不存在外部事务时,rquired会创建一个新的事务,而supports则以非事务的方式执行。

5 not_supported

not_supported:以非事务的模式执行,如果存在外部事物则暂时挂起外部事物

/**
* Execute non-transactionally, suspend the current transaction if one exists.
*/
  • 不存在外部事物,以非事务方式执行
  • 存在外部事物
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() {
    methodB();
    mehtodC();
    methodD();    
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
    TestEntity test = new TestEntity("test-b", "25");
    TestEntity result = testRepository.save(test);
}

public void methodC() {
    TestEntity test = new TestEntity("test-c", "25");
    TestEntity result = testRepository.save(test);
}	

@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
public void methodD() {
    TestEntity test = new TestEntity("test-d", "25");
    TestEntity result = testRepository.save(test);
    throw new RuntimeException("exception in method d");
}
// B,C均失败,D插入成功
// 存在外部事物,D抛出运行时异常导致外部事务A-B-C回滚,而D声明为NOT_SUPPORTED,会使外部事物暂时挂起以非事务的方式执行,不受外部事务的影响。

not_supported和不声明事务的区别:当存在外部事物时,如果一个被调用的方法没有申明事务,其实是处于外部事物中的;而被 not_supported声明的话,会暂时将外部事物挂起,即使外部事务回滚了,也不影响not_supported修饰的方法执行;

存在外部事务时,方法声明为required和不声明事务的效果一样,都是在外部 事务中执行。

6 never

never:强制以非事务的方式执行。如果当前存在事务,则抛出异常

	/**
	 * Execute non-transactionally, throw an exception if a transaction exists.
	 */	
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() {
    methodB();
    mehtodC();
    methodD();    
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
    TestEntity test = new TestEntity("test-b", "25");
    TestEntity result = testRepository.save(test);
}

public void methodC() {
    TestEntity test = new TestEntity("test-c", "25");
    TestEntity result = testRepository.save(test);
}	

@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
public void methodD() {
    TestEntity test = new TestEntity("test-d", "25");
    TestEntity result = testRepository.save(test);
}
//B,C,D均失败
//抛出异常:org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

7 mandatory

mandatory:强制方法在事务中执行,如果当前不存在事务,则抛出异常。

	/**
	 * Support a current transaction, throw an exception if none exists.
	 */
  • 存在外部事物,正常执行
  • 不存在外部事物,抛出异常
public void methodA() {
    methodB();
    mehtodC();
    methodD();    
}

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
    TestEntity test = new TestEntity("test-b", "25");
    TestEntity result = testRepository.save(test);
}

public void methodC() {
    TestEntity test = new TestEntity("test-c", "25");
    TestEntity result = testRepository.save(test);
}	

@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
public void methodD() {
    TestEntity test = new TestEntity("test-d", "25");
    TestEntity result = testRepository.save(test);
}
//B,C,成功,D失败
//B以独立的事务执行;C以非事务的方式执行;D未通过事务校验,没有进入方法体就异常终止执行。
//抛出异常:org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

Spring事务隔离级别

同各种数据库的隔离级别


Spring 事务和 try-catch:

checked 异常:在代码中需要主动捕获或者声明继续往上抛;

unchecked 异常:属于RuntimeException,在编码中无法感知;

通过try-catch可以使异常不被事务切面感知,不影响事务的执行。当异常抛到切面可以感知到的地方时会引起事务的回滚。

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveA() throws Exception {
    TestEntity test = new TestEntity("test-A", "25");
    testMapper.save(test);
    throw new Exception("throw exception in a");
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveB() throws Exception {
    TestEntity test = new TestEntity("test-B", "25");
    testMapper.save(test);
    throw new RuntimeException("throw runtime exception in B");
}

@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class)
public void saveC() {
    TestEntity test = new TestEntity("test-C", "25");
    testMapper.save(test);
    throw new Exception("throw runtime exception in C");
}

@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void saveD() {
    TestEntity test = new TestEntity("test-D", "25");
    testMapper.save(test);
    throw new RuntimeException("throw runtime exception in D ");
}
//A C成功; B D均失败

由上面结果可知:

  • 当@Transactional注解不指定rollbackFor时,默认只对RuntimeException异常回滚,对于checked异常不会回滚;
/**
	 * <p>By default, a transaction will be rolling back on {@link RuntimeException}
	 * and {@link Error} but not on checked exceptions (business exceptions). See
	 * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)}
	 * for a detailed explanation.
	 */
Class<? extends Throwable>[] rollbackFor() default {};

//DefaultTransactionAttribute.rollbackOn(Throwable)
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}
  • 当指定了rollbackFor时,只会对声明的Class和其继承子类的异常进行回滚。

    RuntimieException extends Exception

Spring 事务失效场景:

1 方法所属的bean并没有被spring容器管理

2 数据库引擎不支持事务

3 方法私有

4 自身调用:同一个类中的方法调用

spring的事务管理是通过aop切面实现的,切面的实现是基于代理模式,通过生成一个代理类实现对被代理对象功能的增强。同一个类内的方法间通过this调用,并不会涉及代理类,所以当自身调用时spring的事务管理并不会生效。

5 异常在方法内部被捕获

6 捕获异常类型错误

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值