spring 的声明式事务注解@Transactional,只在抛出RuntimeException异常的时候,才会回滚。只抛出普通Exception的话,不会回滚。如:
@Transactional
public void upgradeTalent(User user, Date updateAt) throws Exception {
TalentLevel levelOne = talentLevelService.getTalentLevel(1);
long levelId = levelOne.getTalentLevelId();
user.setTalentLevelId(levelId);
user.setUpdateAt(updateAt);
user.setUpdateBy(user.getUserId());
user.setUpdateType(1); //前台更新类型
int beans = levelOne.getBeans();
user.setBeans(user.getBeans() + beans);
userService.updateField(user,"talentLevelId,updateAt,updateBy,updateType,beans");
Integer.parseInt("fsdf");
// throw new Exception("非运行期的普通异常");
Talent talent = new Talent();
talent.setCreateAt(updateAt);
talent.setUserId(user.getUserId());
talent.setTalentLevelId(levelId);
talent.setUpdateAt(updateAt);
talent.setUpdateBy(talent.getTalentId());
talent.setUpdateType(1);//前台更新类型
talent.setLastUpgradeAt(updateAt);
long talentId = creat(talent);
}
这个upgradeTalent方法以 Integer.parseInt("fdfs")为界,分为两个事务,userService.updateField和creat(talent),整个upgradeTalent方法加了注解,是一个大事务,要么userService.updateField和creat(talent)同时成功,要么同时失败。
上面的Integer.parseInt("fdfs")方法会抛出NumberFormatException,这个异常是RuntimeException的一个子类,所以事务会回滚,没有更新到User表和Talent表。如果这时候把上面Integer.parseInt("fdfs")改为手动抛出一个非RuntimeException,如改成:throw new Exception("普通异常"),当方法执行到抛出异常这行代码时,即使整个方法加了@Transactional的注解,事务也不会回滚,user表已经被更新,造成脏数据。
所以,在使用spring @Transactional注解的时候,要注意一下。
另外,用Integer.parseInt("fdfs")这句代码,是为了验证发生异常后事务回滚的问题,实际情况可能是其它原因造成异常。
RuntimeException继承Exception
其实Transactional在定义的时候,也说明了事务的使用范围:RetentionPolicy.RUNTIME
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional