Spring中的事务传播机制和嵌套事务

一、序言

Spring中声明式事务确实给我们带来了很大的便利,在Service层方法上带上@Transactional注解即可实现事务,首先看看@Transactional的默认配置:

1、默认使用的事务管理器名字为"transactionManager"。

2、默认事务隔离传播为Propagation.REQUIRED。

3、默认超时时长为底层事务系统的时长。

4、默认事务隔离级别为数据库的隔离级别。

5、默认事务类型为读写事务。

6、默认发生RuntimeExeption异常会触发事务回滚,而受检异常不会。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	
	@AliasFor("transactionManager")
	String value() default "";
	
	@AliasFor("value")
	String transactionManager() default "";

	Propagation propagation() default Propagation.REQUIRED;

	Isolation isolation() default Isolation.DEFAULT;

	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	boolean readOnly() default false;
	
	Class<? extends Throwable>[] rollbackFor() default {};

	String[] rollbackForClassName() default {};

	Class<? extends Throwable>[] noRollbackFor() default {};

	String[] noRollbackForClassName() default {};

}

二、事务传播机制介绍

public enum Propagation {

	
        /**
         * 如果当前线程执行的方法如果没有事务则创建新事务,否则在已存在事务中执行
         * /   
	REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

        /**
         * 如果当前线程执行的方法如果有事务,则在事务中执行,否则无事务执行 
         * / 
	SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

        /**
         * 当前线程执行的方法必须有事务,否则抛出异常
         * /
	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
	
        /**
         * 当前线程执行方法每次都会创建新的事务,如果当前方法已在事务中执行,则会挂起外部事务
         * /
	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
	
        /**
         * 当前线程所执行的方法在无事务环境中执行,如果已有外部事务,则会挂起外部事务
         * /
	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

        /**
         * 当前线程所执行的方法在无事务环境中执行,如果已有外部事务,则会抛出异常
         * /
	NEVER(TransactionDefinition.PROPAGATION_NEVER),

        /**
         * 如果外部事务存在,则当前线程所在方法会在嵌套事务中执行。
         * 如果外部事务不存在,则和REQUIRED效果一样。
         * /
	NESTED(TransactionDefinition.PROPAGATION_NESTED);
}

三、嵌套事务实例

1、Propagation.REQUIRED

@Service
public class TransactionServiceB {

  @Autowired
  private OperationLogMapper operationLogMapper;

  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void methodB() {
    OperationLogDo operationLog = new OperationLogDo();
    operationLog.setOpType(1);
    operationLog.setOpContent("新增操作");
    operationLog.setUserId(1);
    operationLog.setCreateTime(new Date());
    operationLogMapper.saveOperationLog(operationLog);
  }
}


@Service
public class TransactionServiceA {

  @Autowired
  private TransactionServiceB serviceB;

  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void methodA() {
    serviceB.methodB();
    throw new RuntimeException("发生异常了!");
  }
}

由于methodA和methodB事务的传播机制都是Propagation.REQUIRED,因此methodB会和methodA在同一个事务中执行,当methodA执行发生异常时,methodB会回滚,因此methodB中的插入操作不会生效。

2、Propagation.REQUIRES_NEW

@Service
public class TransactionServiceA {

  @Autowired
  private TransactionServiceB serviceB;

  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void methodA() {
    serviceB.methodB();
    throw new RuntimeException("发生异常了!");
  }
}

@Service
public class TransactionServiceB {

  @Autowired
  private OperationLogMapper operationLogMapper;

  @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
  public void methodB() {
    OperationLogDo operationLog = new OperationLogDo();
    operationLog.setOpType(1);
    operationLog.setOpContent("新增操作");
    operationLog.setUserId(1);
    operationLog.setCreateTime(new Date());
    operationLogMapper.saveOperationLog(operationLog);
  }
}

methodA方法上的事务传播机制是Propagation.REQUIRED,而methodB方法上的事务传播机制Propagation.REQUIRES_NEW,因此methodB方法会在一个新的事务中执行,同时外部事务(即methodA所在事务)会被挂起。这里我们看到的效果是数据库中仍然插入了一条记录,说明不受外部事务影响。

3、Propagation.NESTED

@Service
public class TransactionServiceA {

  @Autowired
  private OperationLogMapper operationLogMapper;
  @Autowired
  private TransactionServiceB serviceB;

  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void methodA() {
    serviceB.methodB();
    
    OperationLogDo operationLog = new OperationLogDo();
    operationLog.setOpType(1);
    operationLog.setOpContent("修改操作");
    operationLog.setUserId(1);
    operationLog.setCreateTime(new Date());
    operationLogMapper.saveOperationLog(operationLog);
    
    throw new RuntimeException("发生异常了!");
  }
}

@Service
public class TransactionServiceB {

  @Autowired
  private OperationLogMapper operationLogMapper;

  @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
  public void methodB() {
    OperationLogDo operationLog = new OperationLogDo();
    operationLog.setOpType(1);
    operationLog.setOpContent("新增操作");
    operationLog.setUserId(1);
    operationLog.setCreateTime(new Date());
    operationLogMapper.saveOperationLog(operationLog);
  }
}

此时methodA方法在methodB方法执行完后抛出了异常,最后的结果是methodB中的插入操作回滚了。为什么呢?我们可以把嵌套事务看做是外部事务的一个子事务,既然外部事务发生异常回滚了,那么子事务也会回滚,但要注意子事务不会影响外部事务。而如果methodB上的隔离级别设为REQUIRES_NEW,那么methodB中的插入操作会生效,因为methodB方法执行会在一个新的事务环境中,不受其它事务影响。

有同学会发现,上面的例子即使methodB方法上的事务传播机制为Propagation.REQUIRED,methodB方法中的插入操作也会回滚,那我们再看看Propagation.NESTED和Propagation.REQUIRED的区别,下面再进行改写:

@Service
public class TransactionServiceA {

  @Autowired
  private OperationLogMapper operationLogMapper;
  @Autowired
  private TransactionServiceB serviceB;

  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void methodA() {
    try {
      serviceB.methodB();
    } catch (Exception e) {
      e.printStackTrace();
    }
    
    OperationLogDo operationLog = new OperationLogDo();
    operationLog.setOpType(1);
    operationLog.setOpContent("修改操作");
    operationLog.setUserId(1);
    operationLog.setCreateTime(new Date());
    operationLogMapper.saveOperationLog(operationLog);
  }
}

@Service
public class TransactionServiceB {

  @Autowired
  private OperationLogMapper operationLogMapper;

  @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
  public void methodB() {
    OperationLogDo operationLog = new OperationLogDo();
    operationLog.setOpType(1);
    operationLog.setOpContent("新增操作");
    operationLog.setUserId(1);
    operationLog.setCreateTime(new Date());
    operationLogMapper.saveOperationLog(operationLog);
    throw new RuntimeException("发生异常了!");
  }
}

上面这个例子看到的效果是methodB方法中的插入操作会回滚,而methodA方法在捕获methodB方法抛出的异常后执行插入操作,因此数据库会新增一条记录。这也说明当methodB方法的传播机制为Propagation.NESTED时,methodB方法所在的事务为外部事务的一个子事务,子事务不会影响外部事务。当然如果methodA方法不捕获异常,那么methodA方法中的插入操作同样也会回滚。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凌波漫步&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值