事务失效
事务失效问题
如果只对其中的一段代码添加事务,比方只对其中的**this.save(orders);**一行添加事务
想实现的话有两种方案:
- 使用Spring的编程式事务,这种方式控制粒度更精准,但是缺点是代码耦合度高,需要自己写代码处理事务
- 将需要控制事务的那段代码单独提到一个方法中去,然后仅仅对新抽取的方法控制事务
@Override
public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
//... 省略代码
this.saveOrders(orders);
//... 省略代码
}
//新编写一个方法,将需要事务控制事务的方法,提到这个位置来
@Transactional
public void saveOrders(Orders orders){
this.save(orders);
}
上面的方法可以很好的解决长事务的问题,但是它也引入了一个新的问题,那就是事务失效
@Transactional
public void saveOrders(Orders orders){
this.save(orders);
//手动模拟一个异常,看看事务会不会回滚
int i = 1 / 0;
}
解决方案
运行上面的代码,会发现事务不再自动回滚,原因很简单:那就是这个方法的调用者是原始对象,不是代理对象
而原始对象是没有事务控制能力的,那解决方案就是将调用者再想办法还原为代理对象
@Autowired
private IOrdersCreateService owner;
@Override
public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
//... 省略代码
owner.saveOrders(orders);
//... 省略代码
}
@Transactional
public void saveOrders(Orders orders){
this.save(orders);
//模拟异常
//int i = 1 / 0;
}
接口中也需要对应的暴露新增的方法
/**
* 保存订单
*
* @param orders 订单
*/
void saveOrders(Orders orders);
重新测试,事务问题得到解决
总结
事务失效的情况有哪些?
- 非事务方法内部调用事务方法
- 事务方法没有使用public修饰
- 事务方法的异常在方法内被捕获处理
- 事务方法抛出的异常与rollbackFor属性指定的异常不匹配
- 事务方法配置的事务传播行为有误
① 非事务方法内部调用事务方法
在下面方法中,insertOrderAndReduceStock()方法使用的是原始对象调用的,而不是代理对象调用的
而事务管理功能是代理对象负责的,因此事务失效!
@Service
public class OrderService {
public void createOrder(){
// ... 准备订单数据
// 生成订单并扣减库存
insertOrderAndReduceStock();
}
@Transactional
public void insertOrderAndReduceStock(){
// 生成订单
insertOrder();
// 扣减库存
reduceStock();
}
}
② 事务方法没有使用public修饰
Spring的声明式事务是基于AOP方式结合动态代理来实现的,在其内部有一个类
AbstractFallbackTransactionAttributeSource会检查方法是否使用public修饰,如果不是,则不能进行功能增强
在下面方法中,createOrder()方法没有使用public修饰,因此无法进行事务增强,事务失效!
@Service
public class OrderService {
@Transactional
private void createOrder(){
// ... 准备订单数据
// 生成订单
insertOrder();
// 扣减库存
reduceStock();
}
}
③ 事务方法的异常在方法内被捕获处理
在下面方法中,createOrder方法执行过程中即便出现了异常也不会向外抛出
而Spring的事务管理就是要感知业务方法的异常,当捕获到异常后才会回滚事务
现在事务被捕获,就会导致Spring无法感知事务异常,自然不会回滚,事务就失效了!
@Service
public class OrderService {
@Transactional
private void createOrder(){
// ... 准备订单数据
try {
// 生成订单
insertOrder();
// 扣减库存
reduceStock();
} catch (Exception e) {
// 处理异常
}
}
}
④ 事务方法抛出的异常与rollbackFor属性指定的异常不匹配
Spring的@Transactional使用rollbackFor属性指定当前事务管理器感知的异常类型(默认为RuntimeException)
下面方法createOrder方法抛出了一个IOException,不会被Spring捕获,事务就失效了!
@Service
public class OrderService {
@Transactional(rollbackFor = RuntimeException.class)
public void createOrder() throws IOException {
// ... 准备订单数据
try {
// 生成订单
insertOrder();
// 扣减库存
reduceStock();
} catch (Exception e) {
// 处理异常
throw new IOException();
}
}
}
⑤ 事务方法配置的事务传播行为有误
下面方法reduceStock方法配置了事务传播行为Propagation.REQUIRES_NEW,他代表当前方法会开启一个新事物
也就是与createOrder和insertOrder不在同一个事物中了,因此事务失效!
@Service
public class OrderService1 {
@Autowired
private OrderService2 orderService2;
@Transactional
public void createOrder() {
// 生成订单
orderService2.insertOrder(); // 使用默认的Propagation.REQUIRED传播行为
// 扣减库存
orderService2.reduceStock();// 使用Propagation.REQUIRES_NEW传播行为
}
}
@Service
public class OrderService2 {
@Transactional // 默认传播行为:Propagation.REQUIRED
public void insertOrder() {
// 订单数据插入操作
}
//Propagation.REQUIRES_NEW 代表必须新事物
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void reduceStock() {
// 库存扣减操作
}
}