zoukankan      html  css  js  c++  java
  • Spring Framework中常见的事务传播陷阱(译文)

    最近看到Medium上一篇讨论Spring Framework中事务传播的文章,解释了几种常见的问题,解释的不错,这里直接翻译吧(意译为主,粗体和斜体是我自己加上的)。

    译文:

    这是我的第一篇文章,我打算给大家总结一下开发者在使用Spring事务时,常常会犯的和事务传播相关的错误。

    在这之前,我们先回忆一下Spring中事务是怎样传播的。

    我们先看下Ken Tousen做的有关事务传播注解的笔记(译注:这里指@Transactional注解的propagation属性),这会帮助我们更清楚地记忆。

     

    1. 私有方法

    当公有方法调用私有方法时,不会创建新事务。(译注:这里有点问题,见下面评论)

     1 @Transactional
     2 public class MusicPlayerService {
     3     
     4     @Autowired
     5     Song song;
     6 
     7     @Autowired
     8     MusicCatalogueService musicCatalogueService;
     9 
    10     public void playSong() {
    11         scrobble();
    12         song.play();
    13     }
    14 
    15     @Transactional(propagation = Propagation.REQUIRES_NEW)
    16     private void scrobble() {
    17         musicCatalogueService.scrobble(song);
    18     }
    19 }

    上面这个例子里,如果musicCatalogueService.scrobble(song)调用失败了,playSong()也会失败。

    因为playSong()scrobble()在同一个事务中。

    所以要开启新事务,方法必须声明为public。

     

    译注:

    这里有点问题。即使将scrobble()声明为public,scrobble()也不会开启新事务。

    这是因为Spring是使用AOP来进行事务管理的。而scrobble()和playSong()在一个类里,当playSong()调用scrobble()时,是直接调用scrobble()而不是调用代理方法,所以这两个方法都在同一个事务中。而musicCatalogueService.scrobble(song)是另一个对象(musicCatalogueService)中的方法,这个对象由Spring注入,是可以通过AOP代理的,也就可以通过注解开启新事务(当然前提是,musicCatalogueService的方法有注解)。

     

    小结:

    (1)@Transactional注解对私有方法不起作用,只对公有方法生效。

    (2)Spring使用AOP进行事务管理,因此同一个类中的方法互相调用,在不涉及其他类的方法时,如果第一个方法开启了事务,那么接下来所有的方法都处于同一个事务中(不论其他方法是否私有/公有、是否有注解、注解的传播级别是什么)。

    (3)只有调用不同类的方法时,才有可能开启新事务。

     

    2. 同一个类中的方法调用

    如果一个方法调用另一个方法,而这个被调用方法要开启新事务的话,它必须在另一个类中。

    不然,这两个方法在同一个事务中。

    把方法放在不同的类中来开启新事务的例子:

     1 @Transactional
     2 public class MusicPlayerService {
     3 
     4     @Autowired
     5     Song song;
     6 
     7     @Autowired
     8     MusicCatalogueService musicCatalogueService;
     9 
    10     public void playSong() {
    11         musicCatalogueService.scrobble(song);
    12         song.play();
    13     }
    14 }
    15 
    16 public class MusicCatalogueService {
    17 
    18     @Autowired
    19     ScrobbleRepository scrobbleRepository;
    20 
    21     @Transactional(propagation = Propagation.REQUIRES_NEW)
    22     public void scrobble(Song song) {
    23         scrobbleRepository.save(song);
    24     }
    25 }

    3. SUPPORTS和readonly结合

    如果没有正在进行的事务,那么SUPPORTS不会创建新事务。readonly会被忽视。

    4. CRD操作和readonly结合

     如果我们使用了REQUIRED或者REQUIRES_NEW,以及readonly,然后尝试做CREATE/DELETE/UPDATE操作,那么程序会抛出只读异常。

     

    5. Checked Exceptions

    如果程序抛出RuntimeException,事务会回滚。这很合理。

    但是如果程序抛出的是checked exceptions呢?

    答案是:事务不会回滚。

    那怎样去处理这种情况?

    Spring提供了rollbackFor属性。通过指定要回滚的异常类型,可以确保在程序抛出checked exceptions时事务回滚。用法如下:

    @Transactional(rollbackFor = Exception.class)
    public void rollBackActionDefinedMethod() {
    }

    译文完。

     

  • 相关阅读:
    Devpexpress 打印预览问题
    常用DOS命令
    C# datetimePicker控件格式设置
    DevExpress中GridControl的属性设置
    C++ 学习1
    layui 时间控件选择一闪就消失,打不开问题解决办法
    echarts报错,Uncaught Error: series.type should be specified?
    vue项目中,在mian.js文件中引入scss文件后,报错
    记一次在vue中使用scss报错
    使用hexo搭建个人博客时引入图片失败
  • 原文地址:https://www.cnblogs.com/huangzejun/p/10387215.html
Copyright © 2011-2022 走看看