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

相关推荐