事务传播
多个事务方法相互调用时,事务如何在这些方法间传播。
方法A是一个事务的方法,方法A执行过程中调用了方法B。
- 此时方法B可能有事务,也可能没有事务。如果有事务的话。
- 方法B对事务的要求不同都会对方法A的事务具体执行造成影响。
- 同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就由两个方法所定义的事务传播类型所决定。
举一个例子:
我需要预定一个掘金背包,预定动作的同时,我需要支付一笔钱,这是两个动作,如果我支付失败了,那么我的预定也失败了!
这个例子实现很简单,只需要将两个Service层都开启事务即可
使用注解的核心代码
前置需要:
- 需要配置类,注入
DataSource
、JdbcTemplate
、DataSourceTransactionManager
,开启@EnableTransactionManagement
启用事务- 需要配置两个对应的
Dao
创建订单:
jdbcTemplate.update("insert into `order` (ordername) values (?)",orderName);
创建支付:
jdbcTemplate.update("insert into `pay` (pay_money) values (?)",money);
我们只关注Service
层
- 预定的动作
@Service
@Transactional
public class OrdersService {
@Autowired
OrderDao orderDao;
@Autowired
PayingService payingService;
public void overbooking(String orderName){
orderDao.overbooking(orderName);
payingService.paying(100);
}
}
我们可以发现在overbooking
中调用了PayingService.paying()
方法。
- 支付的动作
@Service
@Transactional
public class PayingService {
@Autowired
PayDao payDao;
public void paying(Integer money){
payDao.paying(money);
int i = 1/0;
}
}
两个方法都开启了事务,而且在paying
方法中出现了int i =1/0
的错误,这时候事务起作用,都会执行rollback
,两条SQL都失效了。
需求变更
这时候有人突然会说了,那我支付失败可能是这个银行卡里没钱了,但是另外一个银行卡有钱,所以你不能因为我第一次交费失败了就把我预定给整没了,你应该给我5分钟时间重新操作,超时了再取消呗!
那原题就变成了:
我需要预定一个掘金背包,预定动作的同时,我需要支付一笔钱,这是两个动作,如果我支付失败了,那么系统保留订单,但支付金额的操作需要回退!
这个需求就需要用到事务传播的控制了!
解决办法
先看注解的解决办法,在@Transactional
注解中有个属性是:propagation
,它有如下几个值:
- REQUIRED
- SUPPORTS
- MANDATORY
- REQUIRES_NEW
- NOT_SUPPORTED
- NEVER
- NESTED
这里我们需要用到的是NOT_SUPPORTED
,也就是在OrdersService
的@Transactional
注解中加上该属性
@Transactional(propagation = Propagation.NOT_SUPPORTED)
再次执行测试时,就会发现,数据库中添加订单的操作成功了;而订单支付的动作失败了。
这就是事务传播控制的魅力(虽然可能这个例子不太恰当,可能存在漏洞,也是助于理解嘛)。