关于@Transactional使用的疑问:双表更新如何保证事务原子性
关于Spring @Transactional注解使用的三个问题解答
以下回答基于Spring框架声明式事务的默认实现逻辑:
问题1:使用@Transactional实现两张表更新的原子性需求是否正确?
是正确的。
Spring声明式事务的核心作用就是将多个数据库操作绑定到同一个事务中,默认配置下:
- 事务内任意一步抛出未捕获的
RuntimeException或Error时,会自动触发全量回滚,完全满足「table A更新失败(包括代码抛异常、数据库操作失败)则table B更新不生效」的一致性要求 - 注意如果需要对检查异常也触发回滚,需要显式配置注解属性:
@Transactional(rollbackFor = Exception.class)
问题2:仅在方法级别添加@Transactional是否能正常生效?
可以正常生效。
- 类级别添加@Transactional的作用是给当前类的所有public方法添加默认事务配置,属于批量配置的手段
- 方法级别的@Transactional配置优先级高于类级别,单独给需要开启事务的public方法加注解即可实现需求,不需要额外在类上加
注意:以上生效的前提是,加注解的方法是public修饰,且是由外部类调用该方法,不是当前类内部自调用。
问题3:给私有方法updateValue加@Transactional是否能实现仅两个update操作在事务内?
完全不能生效,这个注解不会起任何作用,原因如下:
Spring的@Transactional是基于AOP动态代理实现的,只有代理对象拦截到方法调用时才会开启、提交/回滚事务,而你的写法有两个致命问题:
@Transactional根本不支持非public方法,私有方法上的注解会被直接忽略- 你是在类内部的
updateMethod中直接调用本类的updateValue,属于类内部自调用,不会走代理对象,即使把方法改成public,注解也不会生效
如果要实现「tableADao.get不纳入事务、两个update纳入同一个事务」的需求,你可以调整写法:
- 把
updateValue改成public方法 - 调用时用代理对象调用,示例调整后代码如下:
// 首先开启暴露代理对象的配置:启动类加@EnableAspectJAutoProxy(exposeProxy = true) public void updateMethod(final Long userId, Integer value) { //some processing Integer value = tableADao.get(userId); //some processing // 用代理对象调用事务方法,才会触发事务拦截 ((当前类名) AopContext.currentProxy()).updateValue(value); //some processing } @Transactional(rollbackFor = Exception.class) public void updateValue(final Integer value) { tableADao.updateValue(value); tableBDao.updateValue(value); }
内容的提问来源于stack exchange,提问作者once
相关产品推荐
相关产品推荐

