在一些业务场景中,为了保持业务数据的一致性,需要用到事务,特别是交易系统。但是其中部分逻辑,比如订单关闭,需要自成一个独立的事务,最重要的是不能受到外部事务调用的影响(比如外部的异常,不能导致订单关闭子逻辑的回滚)。Spring使用事务传播的特性,完美地解决这个问题
Spring事务传播
Spring在TransactionDefinition
接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。这是Spring为我们提供的强大的工具箱,使用事务传播行可以为我们的开发工作提供许多便利。
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
根据Spring事务传播的类型,可以看出
PROPAGATION_REQUIRES_NEW
是可以满足我们的需求的
业务实现
这里我们假设创建订单时,需要完成了两步(插入用户,插入商品,这里没用关闭订单做示例了,因为逻辑比较复杂),其中插入用户这一步,自身的事务特性不受外部影响,所以使用Propagation.REQUIRES_NEW
- UserManager
@Component
public class UserManager {
@Resource
private UserMapper userMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insert(User user) {
userMapper.insert(user);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertWithException(User user) {
userMapper.insert(user);
throw new RuntimeException("foo");
}
}
- ProductManager
@Component
public class ProductManager {
@Resource
private ProductMapper productMapper;
@Transactional(propagation = Propagation.REQUIRED)
public void insert(Product product) {
productMapper.insert(product);
}
}
验证
- 外部异常:用户信息插入成功,商品信息插入失败
@Component
public class OrderManager {
@Resource
private UserManager userManager;
@Resource
private ProductManager productManager;
@Transactional(rollbackFor = Exception.class)
public void createOrder(String userName, String productName) {
User user = new User(null, userName);
//插入用户信息
userManager.insert(user);
Product product = new Product(null, productName);
//插入商品信息
productManager.insert(product);
throw new RuntimeException("fail to create order");
}
}
- 插入用户异常:用户信息插入失败,商品信息插入失败
@Transactional(rollbackFor = Exception.class)
public void createOrder(String userName, String productName) {
User user = new User(null, userName);
userManager.insertWithException(user);
Product product = new Product(null, productName);
productManager.insert(product);
throw new RuntimeException("fail to create order");
}
网友评论