使用Spring的@Transactional进行事务管理

前言

近段时间在重构代码的时候,发现一个严重的bug:事务方法报错之后,竟然不回滚,原因是我把异常捕获了,具体原因下文会讲。细思恐极,整个项目都是那种写法。于是,开始对@Transactional注解进行研究和测试。
@Transactional能够帮我们自动管理事务。一般在service层的方法中加上该注解。但是有很多坑!以下的代码均为不成形的代码,俗称伪代码。

坑一:只对外部调用有效

内部调用事务方法会导致事务无效,原因是spring使用动态代理进行事务管理,只能对外部调用进行拦截,内部调用的管不了。

class UserController {
    // 外部调用,受事务管理
    userService.saveUser();
}
class UserService {
    @Transation
    public void saveUser() {}
    
    public void saveUserInternal() {
        // 内部调用,不受事务管理
        saveUser();
    }
}

解决办法:获取代理对象,并通过代理对象去调用
第一步,开启暴露代理

<aop:aspectj-autoproxy expose-proxy="true" />

第二步,通过代理对象去调用事务方法

((UserService)AopContext.getProxy()).saveUser();
坑二:只对public方法有效

因为事务注解只对外部调用有效(参考坑一),private方法只能内部调用,所以private无效很好理解。但是protected为什么无效。stackoverflow的这个回答解释了我的困惑。
在这里插入图片描述
意思是说:JDK代理不支持对protected方法的拦截,但是CGLIB是可以的,但是不推荐使用,然后Spring那帮家伙为了统一,直接砍掉了protected方法的支持。

坑三:只对非受检异常有效

什么是受检异常和非受检异常?简单来讲,受检异常就是如果不捕获异常或抛出异常,那么编译会报错,常见的如FileInputStream,你要么捕获异常,要么抛出异常。
在这里插入图片描述
解决方法:@Transactional(rollbackFor = Exception.class)

@Transactional(rollbackFor = Exception.class)
public void saveFile() throws FileNotFoundException {
	// 数据操作
	userDao.saveUser();
    // 会抛出异常,因为设置了rollbackFor属性,会回滚
	InputStream in = new FileInputStream(new File("xxx.xxx");
}
坑四:只对抛出异常有效

异常捕获了,相当于隐藏了异常,出现异常不会自动回滚。如果一定要捕获,请在捕获代码块里手动回滚

@Transactional
public void saveUser() {
    try {
        saveUser();   
    } catch(Exception e) { 
		// 手动关闭事务
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}
悬而未决

鄙人在调试的时候遇到一个问题,在事务方法里,先删除后查询,是正常的;但是先保存后查询,就查询不到保存的数据。各位大佬,如果知道是什么原因可以点播下。

资料
  1. @Transactional事务传播行为

  2. 为什么@Transactional不能用在protected方法上?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值