一、序言
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("发生异常了!"); } }
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); } }
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方法中的插入操作同样也会回滚。