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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/cblstc/article/details/94582905

前言

近段时间在重构代码的时候,发现一个严重的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方法上?

猜你喜欢

转载自blog.csdn.net/cblstc/article/details/94582905