jdbcTemplate与JPA 混合事务
问题阐述
简单的场景示例如下:
需求:修改学生的姓名和年龄。
说明:因为历史遗留问题,或者一些其他原因,导致 姓名 由jpa完成修改,年龄由jdbcTemplate完成修改。
但是两者存在事务的原子性关联,也就是说两者必须一起成功,或者一起失败。
伪码示例:
public boolean updateUserInfo(User user){
//1.jpa 修改姓名
int r1 = userRepository.updateUserNameById(user.getId(),user.getUserName());
//模拟异常发生
int i = 10/0;
//2.jdbcTemplate 修改年龄
int r2 = jdbcTemplate.update("update user set age=? where id=?",参数略);
}
如果不使用事务的话,可能只修改了姓名,但是年龄没有被修改。不符合一致性。
如果使用事务,怎么保证两者在同一个事务当中呢?
解决办法
这里使用到事务的传播行为。
我们经常用到Spring的@Transactional注解中,有一个属性是 Propagation propagation() default Propagation.REQUIRED;
默认情况下,传播行为就是 REQUIRED。这表示:
如果有事务在运行,当前方法就在这个事务内运行
否则就新创建一个事务,并在自己的事务内运行
对于我们上面的问题,我们需要让两个操作在同一个事务当中,那么实际上,使用默认的REQUIRED传播行为就可以解决。
我们在业务方法上加上一个事务,这个事务就是外层事务。
执行代码时,jpa会尝试开启一个事务,但是此时发现已经在外层事务当中,根据传播行为,就直接使用外层事务。
jdbcTemplate 的同理。
(事实上我对这块理解不深,但是测试下来确实是可行的)
那么优化后代码就是:
@Transactional
public boolean updateUserInfo(User user){
//1.jpa 修改姓名
int r1 = userRepository.updateUserNameById(user.getId(),user.getUserName());
//模拟异常发生
int i = 10/0;
//2.jdbcTemplate 修改年龄
int r2 = jdbcTemplate.update("update user set age=? where id=?",参数略);
}
发现好像没做什么特殊操作,因为默认的传播行为就是REQUIRED,所以没写。
执行一下可以发现,一旦发生异常,确实两者都进行了回滚。即使异常