背景
CR时看到在spring中为了减少事务范围,使用编程式事务来替代声明式事务,由此启发研究了下各自的特点。实际场景如下(脱敏代码)
@Override
public void resultNotify(AuthModel model) {
//1.状态检查
verifyModel verifyModel = repository.queryVerify(model.getVerifyId());
if (verifyModel == null) {
LOG.warn("verify record not exist,verifyId:{}", verifyId);
return;
}
//2.更新记录及状态
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//2.1 更新状态
int updateRow = repository.updateVerifyById(verifyModel);
if (updateRow != 1) {
throw new RuntimeException("update verify result fail");
}
//2.2 更新状态
repository.updateStatus(verifyModel);
}
});
}
问题
- 如何选择使用声明式还是编程式事务
注意
Spring声明式事务失效的场景
- 数据库引擎不支持事务
- 注解所在类没有被加载为Bean
- 注解所在方法是非public方法
- 发生了自调用的情况(经典问题,未经过spring的代理),一种解法
- 数据源未配置事务管理器
- 事务的传播行为设置为了
Propagation.NOT_SUPPORTED
- 异常被吃掉了
- 异常类型不正确,默认只对
RuntimeException
进行回滚
分析
声明式
@Transactional
@Override
public void updatePersonAndUserInfo(Person person,User user) {
personDao.updatePersonAge(person);
userDao.updateUserCard(user);
}
编程式
@Resource
private TransactionTemplate transactionTemplate;
@Override
public void updateUserAndMonsterInfo(Person person, User user) {
monsterDao.updateMonsterLevel(1, 110);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//userDao.updateUserCard(user);
updateUserCard(user);
personDao.updatePersonAge(person);
}
});
}
private void updateUserCard(User user) {
userDao.updateUserCard(user);
}
结论&经验
声明式事务实现起来更加简单,不用关注过多细节,只需要在接口方法上增加相应声明即可,但很多时候业务逻辑是复杂的,可能涉及多个数据库表的操作,且对各自的事务要求不同,在复杂场景下需要精细的控制事务的粒度和范围时,编程式事务更加灵活。
网友评论