protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {......try {// This is an around advice: Invoke the next interceptor in the chAIn.// This will normally result in a target object being invoked.retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exception// 捕获到异常,进行回滚操作,如果我们在业务方法已经捕获掉异常,这里就捕获不到了,自然就不会回滚了completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}......return result;} }
可以看到,只有捕获到异常时才进行回滚操作,如果我们在业务方法已经捕获掉异常,这里就捕获不到了,自然就不会回滚了 。
修正方式:就是对异常捕获尽量做到局部针对操作,不要笼统把整个方法的代码逻辑都包括进行,这样异常就抛出去了 。
3.3 @Transactional 属性 rollbackFor 设置错误,导致异常不满足回滚条件直接看代码:
@Transactionalpublic void addUser(UserParam param) {User user = PtcBeanUtils.copy(param, User.class);.......// 添加用户角色this.addUserRole(user.getId(), param.getRoleIds());log.info("执行结束了");}public void addUserRole(Long userId, List<Long> roleIds) throws Exception {if (CollectionUtils.isEmpty(roleIds)) {return;}List<UserRole> userRoles = new ArrayList<>();roleIds.forEach(roleId -> {UserRole userRole = new UserRole();userRole.setUserId(userId);userRole.setRoleId(roleId);userRoles.add(userRole);});userRoleDAO.insertBatch(userRoles);throw new Exception("发生异常咯");}
这里#addUser()使用@transactional,但没有设置rollbackFor属性,且#addUserRole()抛出的异常是exception,不是RuntimeException,这样事务也失效了,因为默认情况下,出现 RuntimeException(非受检异常)或 Error 的时候,Spring才会回滚事务
从上面3.2小节的completeTransactionAfterThrowing(txInfo, ex);进去完成回滚操作会判断异常类型是否满足规定,DefaultTransactionAttribute 类能看到如下代码块,可以发现相关证据,通过注释也能看到 Spring 这么做的原因,大概的意思是受检异常一般是业务异常,或者说是类似另一种方法的返回值,出现这样的异常可能业务还能完成,所以不会主动回滚;而Error 或 RuntimeException 代表了非预期的结果,应该回滚:
public boolean rollbackOn(Throwable ex) {return (ex instanceof RuntimeException || ex instanceof Error); }
修正方法:设置rollbackFor:@Transactional(rollbackFor = Exception.class)
3.4 @Transactional 应用在非 public 修饰的方法上@Transactional(rollbackFor = Exception.class)private void addUserRole(Long userId, List<Long> roleIds) {if (CollectionUtils.isEmpty(roleIds)) {return;}List<UserRole> userRoles = new ArrayList<>();roleIds.forEach(roleId -> {UserRole userRole = new UserRole();userRole.setUserId(userId);userRole.setRoleId(roleId);userRoles.add(userRole);});userRoleDAO.insertBatch(userRoles);throw new RuntimeException("发生异常咯");}
idea也会提示爆红:
文章插图
图片
Spring通过CGLIB动态代理来增强生产代理对象,CGLIB 通过继承方式实现代理类,private 方法在子类不可见,自然也就无法进行事务增强 。s在基于AOP事务控制实现原理一文中也分析过,会调用到AbstractFallbackTransactionAttributeSource的computeTransactionAttribute()方法
@Nullable protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}...... }
修正方式:自然是改成public3.5 @Transactional 注解传播属性 propagation 设置错误如上面我们新增的用户的同时要添加用户角色,但是假如我们希望即使添加角色错误了,还可以正常新增用户 。
public void addUser(UserParam param) {String username = param.getUsername();checkUsernameUnique(username);User user = PtcBeanUtils.copy(param, User.class);// 添加用户userDAO.insert(user);// 添加用户角色userRoleService.addUserRole(user.getId(), param.getRoleIds());}
#userRoleService.addUserRole()@Transactional(rollbackFor = Exception.class)private void addUserRole(Long userId, List<Long> roleIds) {if (CollectionUtils.isEmpty(roleIds)) {return;}List<UserRole> userRoles = new ArrayList<>();roleIds.forEach(roleId -> {UserRole userRole = new UserRole();userRole.setUserId(userId);userRole.setRoleId(roleId);userRoles.add(userRole);});userRoleDAO.insertBatch(userRoles);throw new RuntimeException("发生异常咯");}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Springboot+Redisson封装分布式锁Starter
- 项目开展 CICD 的实践探路
- SpringBoot项目中异步调用接口方式知多少?
- 车险保什么 车险保什么项目最合算
- word怎么添加项目符号 word怎么添加项目符号双引号
- Spring Data JPA 和 MyBatis 谁更强?
- 把Android手机变成电脑摄像头,开发者倒苦水:40行代码搞定,但需要40个项目文件支持!
- Springboot默认的错误页是如何工作及工作原理你肯定不知道?
- SpringBoot通过一个注解结合Redis实现接口限流就是这么简单
- 安可项目需要什么资质 安可项目是什么意思