本章主要讨论 Spring Transaction 事务的 7 种传播机制,看了网上大部分文章都是一些概念,看完之后似懂非懂,为了真正的了解这几种传播机制,本文中会对每种传播机制有实际的代码进行演示和总结,以此来加深理解。
其实 Spring 源码中就有详细的解释,代码位置:
源码如下(省略了注释,注释中会对每种方式做简要的说明,下面文章我们会进行讨论):
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),
NESTED(TransactionDefinition.PROPAGATION_NESTED);
private final int value;
Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
https://docs.spring.io/spring-framework/docs/5.2.8.RELEASE/spring-framework-reference/data-access.html#tx-propagation
官网只对 REQUIRED、REQUIRES_NEW、NESTED 三种做了说明。
以下所有示例均按照 caller -> method-1->method-2 的方式来进行演示,与官网保持一致
注意:@Transactional 限制
我们先写一个非事务版本,代码如下:
UserController
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("addUser")
public String addUser() {
// method-1
userService.addUser(new User("小明", 18));
return "success";
}
}
UserImpl
@Service
public class UserImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public void addUser(User user) {
userMapper.addUser(user);
// method-2
user2Service.addUser(user);
}
}
User2Impl
@Service
public class User2Impl implements User2Service {
@Autowired
private UserMapper userMapper;
@Override
public void addUser(User user) {
User newUser = new User(user.getName() + "-new", 18);
userMapper.addUser(newUser);
}
}
浏览器输入:http://127.0.0.1:8185/addUser
执行结果如下:
两条数据正常插入。
UserController
@GetMapping("addUserException")
public String addUserException() throws Throwable {
// method1
userService.addUserException(new User("小明", 18));
return "success";
}
UserImpl
@Override
public void addUserException(User user) throws Exception {
userMapper.addUser(user);
// method-2
user2Service.addUserException(user);
}
User2Impl
@Override
public void addUserException(User user) throws Exception {
User newUser = new User(user.getName() + "-exception", 18);
int i = 1 / 0;
userMapper.addUser(newUser);
}
浏览器输入:http://127.0.0.1:8185/addUserException
执行结果:
只有第一条插入成功。
Support a current transaction, create a new one if none exists.
This is the default setting of a transaction annotation.
支持当前事务,如果当前事务不存在,创建一个新的事务。事务注解 @Transactional
的默认设置。
从上图中可以看到,method 1 和 method 2 是用的同一个 Transaction 事务。
UserController
@GetMapping("addUserTransactional/required")
public String addUserTransactionalRequired() throws Exception {
// method-1
userService.addUserTransactionalRequired(new User("小明required", 18));
return "success";
}
UserImpl
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void addUserTransactionalRequired(User user) throws Exception {
userMapper.addUser(user);
// method-2
user2Service.addUserTransactionalException(user);
}
User2Impl
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void addUserTransactionalException(User user) throws Exception {
User newUser = new User(user.getName() + "-exception", 18);
userMapper.addUser(newUser);
throw new RuntimeException("异常测试");
}
浏览器输入:http://127.0.0.1:8185/addUserTransactional/required
执行结果:数据库无新增数据
method-2 抛出异常,method-1 和 method-2 事务一起回滚。
同样的我们可以把异常在 method-1中抛出
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void addUser1TransactionalRequired(User user) throws Exception {
userMapper.addUser(user);
// method-2
user2Service.addUserTransactionalException(user);
throw new RuntimeException("异常测试");
}
执行结果:数据库无新增数据
method-1 抛出异常,method-1 和 method-2 事务一起回滚。
method-1(有事务)调用 method-2(有事务)
REQUIRED 这种默认的传播机制比较常用,符合我们一般的业务场景,多个操作要么一起成功,要么一起失败,保证业务操作的一致性。
Create a new transaction, and suspend the current transaction if one exists.
创建一个新的事务,如果当前事务存在,则挂起当前事务。
从上图中可以看到,每一个方法都会创建一个新的 Transaction 事务。由此可以猜到,method-1 如果抛出异常,method-1 的事务会进行回滚,但是并不会影响 method-2,下面我们用代码来进行验证。
PS:测试结果与猜想的有点小差别。
UserController
@GetMapping("addUserTransactional/requires_new")
public void addUserTransactionalRequiresNew() throws Exception {
// method-1
userService.addUserTransactionalRequiresNew(new User("小明requires_new", 18));
}
UserImpl
@Override
public void addUserTransactionalRequiresNew(User user) {
userMapper.addUser(user);
// method-2
user2Service.addUserTransactionalRequiresNewException(user);
}
User2Impl
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void addUserTransactionalRequiresNewException(User user) throws Exception {
User newUser = new User(user.getName() + "-exception", 18);
userMapper.addUser(newUser);
throw new RuntimeException("异常测试");
}
浏览器输入:http://127.0.0.1:8185/addUserTransactional/requires_new
执行结果:数据库无新增数据
method-2 抛出异常,method-2先回滚, method-1 接收到 method-2 的异常,也进行回滚。
我们把异常移动到 method-1 中
UserImpl
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void addUser1TransactionalRequiresNew(User user) throws Exception {
userMapper.addUser(user);
// method-2
user2Service.addUserTransactionalRequiresNewException(user);
throw new RuntimeException("异常测试");
}
执行结果:
method-2 执行成功,插入了记录;method-1 出现异常,进行回滚,但是并不影响 method-2。
method-1(有事务)调用 method-2(有事务)
Support a current transaction, execute non-transactionally if none exists.
支持当前事务,如果当前事务不存在,则以非事务方式执行。
userController
@GetMapping("addUserTransactional/supports")
public void addUserTransactionalSupports() throws Exception {
// method-1
userService.addUserSupports(new User("小明supports", 18));
}
UserImpl
@Override
public void addUserSupports(User user) {
userMapper.addUser(user);
// method-2
user2Service.addUserTransactionalSupportsException(user);
}
User2Impl
@Override
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void addUserTransactionalSupportsException(User user) {
User newUser = new User(user.getName() + "-exception", 18);
userMapper.addUser(newUser);
throw new RuntimeException("异常测试");
}
http://127.0.0.1:8185/addUserTransactional/supports
执行结果:
method-1 执行成功,插入了记录;method-2 出现异常,但是也插入成功了,因为 method-1 不存在事务,所以 method-2 以非事务方式执行。
如果我们把 method-1 中增加事务
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void addUserSupports(User user) {
userMapper.addUser(user);
// method-2
user2Service.addUserTransactionalSupportsException(user);
}
再次执行,数据库无新增数据,表示 method-2 异常,method-1 和 method-2 一起回滚。
method-1(无事务)调用 method-2(有事务)
method-1(有事务)调用 method-2(有事务)
Support a current transaction, throw an exception if none exists.
支持当前事务,如果不存在事务,则抛出异常。
UserController
@GetMapping("addUserTransactional/mandatory")
public void addUserTransactionalMandatory() throws Exception {
// method-1
userService.addUserMandatory(new User("小明mandatory", 18));
}
UserImpl
@Override
public void addUserMandatory(User user) {
userMapper.addUser(user);
// method-2
user2Service.addUserMandatory(user);
}
User2Impl
@Override
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
public void addUserMandatory(User user) {
User newUser = new User(user.getName() + "-new", 18);
userMapper.addUser(newUser);
}
执行结果:
抛出异常
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
method-1(无事务) 调用 method-2(有事务)
No existing transaction found for transaction marked with propagation 'mandatory'
;method-1(有事务) 调用 method-2(有事务)
Execute non-transactionally, suspend the current transaction if one exists.
以非事务方式执行,如果存在当前事务,则挂起当前事务。
UserController
@GetMapping("addUserTransactional/not_supported")
public void addUserTransactionalNotSupported() throws Exception {
// method-1
userService.addUserNotSupports(new User("小明not_supported", 18));
}
UserImpl
@Override
public void addUserNotSupports(User user) {
userMapper.addUser(user);
// method-2
user2Service.addUserTransactionalNotSupportsException(user);
}
User2Impl
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
public void addUserTransactionalNotSupportsException(User user) {
User newUser = new User(user.getName() + "-exception", 18);
userMapper.addUser(newUser);
throw new RuntimeException("异常测试");
}
执行结果:
method-2 抛出异常,但是插入成功,因为以非事务的方式执行,method-1 本来就没有事务,所以也执行成功。
如果我们给 method-1 增加事务
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void addUserNotSupports(User user) {
userMapper.addUser(user);
// method-2
user2Service.addUserTransactionalNotSupportsException(user);
}
执行结果:
method-2 抛出异常,但是插入成功,因为以非事务的方式执行,但是 method-1 有事务,获取到 method 抛出的异常,进行回滚。
method-1(无事务) 调用 method-2(有事务)
method-1(有事务) 调用 method-2(有事务)
Execute non-transactionally, throw an exception if a transaction exists.
以非事务方式执行,如果存在事务则抛出异常。
UserController
@GetMapping("addUserTransactional/never")
public void addUserTransactionalNever() throws Exception {
// method-1
userService.addUserNever(new User("小明never", 18));
}
UserImpl
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void addUserNever(User user) {
userMapper.addUser(user);
// method-2
user2Service.addUserTransactionalNever(user);
}
User2Impl
@Override
@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
public void addUserTransactionalNever(User user) {
User newUser = new User(user.getName() + "-new", 18);
userMapper.addUser(newUser);
}
执行结果:
抛出异常
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
method-1(有事务) 调用 method-2(有事务)
Existing transaction found for transaction marked with propagation 'never'
;
method-1(无事务) 调用 method-2(有事务)
Execute within a nested transaction if a current transaction exists,
behave like {@code REQUIRED} otherwise.
如果当前事务存在,则在嵌套事务中执行,否则行为类似 REQUIRED。
UserController
@GetMapping("addUserTransactional/nested")
public void addUserTransactionalNested() throws Exception {
// method-1
userService.addUserNested(new User("小明nested", 18));
}
UserImpl
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void addUserNested(User user) {
userMapper.addUser(user);
user2Service.addUserTransactionalNested(user);
throw new RuntimeException("异常测试");
}
User2Impl
@Override
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void addUserTransactionalNested(User user) {
User newUser = new User(user.getName() + "-exception", 18);
userMapper.addUser(newUser);
// throw new RuntimeException("异常测试");
}
执行结果:数据库无新增数据
不论异常是在 method-1 或者 method-2 中抛出,都会一起回滚。
method-1(有事务) 调用 method-2(有事务)
method-1(无事务) 调用 method-2(有事务)
从两者的示例结果分析来看,这两个没有区别;
我们把上面的两个例子再改造一下,给 method-2 增加 try catch 语句来捕捉异常
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void addUserTransactionalRequired(User user) throws Exception {
userMapper.addUser(user);
// method-2
try {
user2Service.addUserTransactionalException(user);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void addUserNested(User user) {
userMapper.addUser(user);
try{
user2Service.addUserTransactionalNested(user);
}catch (Exception e){
e.printStackTrace();
}
}
执行 REQUIRED 结果:
抛出异常
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
执行 NESTED 结果:
method-1 执行成功,method-2 回滚。
总结一下差别: