一、序言
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方法中的插入操作同样也会回滚。