前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java的@Transactional事务回滚

Java的@Transactional事务回滚

作者头像
全栈程序员站长
发布2022-07-25 08:46:35
2.1K0
发布2022-07-25 08:46:35
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

@Transactional 基本原理概述

在应用系统调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务。

处理Springboot下提交事务异常,数据库没有回滚的问题

Spring文档中说道,Spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作。

什么是检查型异常和非检查型异常?

最简单的判断点有两个: 1、继承自runtimeException或error的是非检查型异常,而继承自exception的是检查型异常。 2、对非检查型异常可以不用捕获,而检查型异常必须用try语句块进行处理或者把异常交给上级方法处理,总之就是必须写代码处理它。所以必须service捕获异常,然后再次抛出,这样事务才能生效。

默认规则:

1、让检查型异常也回滚,@Transactional(rollbackFor=Exception.class),一般只需添加这个即可

2、让非检查型异常不回滚,@Transactional(notRollbackFor=RunTimeException.class)

3、不需要事务管理的(就是只是查询用)方法,@Transactional(propagation=Propagation.NOT_SUPPORTED),或者不添加

4、手动回滚,TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 例如:

代码语言:javascript
复制
try {
        String name = (String) list.get(j - 1).get("name");
      } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        return ResultUtil.error(500, "文件解析错误");
       }

你需要注意的事

1.@Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能 2.Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。 3.当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,还是在具体的类上使用 @Transactional 注解比较好。 4.避免 Spring 的 AOP 的自调用问题:自调用就是方法A内调用本类的另一个加上事务注解的方法B时,方法B中对数据库的操作是不带事务的。

Spring AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚。 失效原因:

在这里插入图片描述
在这里插入图片描述

方法one方法two都是public的:

classA中 ,任意要调用classB的方法,是通过spring代理的方式,那么spring的注解才会生效 classA中,方法one 调用同class内的方法two,即this调用,spring注解不会生效(例如@Cachable,@Transaction)

解决方法

方案一:使用AspectJ代理

代码语言:javascript
复制
@Service
public class OrderService { 
   
    private void insert() { 
   
        insertOrder();
    }
@Transactional
    public void insertOrder() { 
   
        //insert log info
        //insertOrder
        //updateAccount
       }
}

insertOrder 尽管有@Transactional 注解,但它被内部方法 insert 调用,事务被忽略,出现异常事务不会发生回滚。

上面的两个问题@Transactional 注解只应用到 public 方法和自调用问题,是由于使用 Spring AOP 代理造成的。为解决这两个问题,可以使用 AspectJ取代 Spring AOP 代理,但现在有更好的解决方法。

方案二:利用AopContext.currentProxy()方法获得代理

方法的意思是尝试返回当前AOP代理。这种做法非常简洁,但是在默认情况下是不起作用的!因为AopContext中拿不到currentProxy,会报空指针。需要一些额外的配置,但不能对所有的注解拦截都有效,这是因为这些注解不是用的AspectJ代理,如果是@Transactional事务注解的话, 则是生效的,具体细节要翻源码了,这里不推荐使用。

方案三:通过ApplicationContext来获得动态代理对象(推荐)

代码语言:javascript
复制
@Component
public class AsyncService implements ApplicationContextAware { 
   

    private ApplicationContext applicationContext;

    public void async1() { 
   
        System.out.println("1:" + Thread.currentThread().getName());
        // 使用AppicationContext来获得动态代理的bean,然后再执行你调用的方法
        this.applicationContext.getBean(AsyncService.class).async2();
    }

    @Async
    public void async2() { 
   
        System.out.println("2:" + Thread.currentThread().getName());
    }

    // 注入ApplicationContext
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
   
        this.applicationContext = applicationContext;
    }
}

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/127133.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年4月9,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • @Transactional 基本原理概述
  • 处理Springboot下提交事务异常,数据库没有回滚的问题
  • 默认规则:
  • 你需要注意的事
  • 解决方法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档