spring抛异常之后的事务回滚
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