JAVA 基础 之 事务的传播与隔离机制

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。这是Spring为我们提供的强大的工具箱,使用事务传播可以为我们的开发工作提供许多便利。
事务传播行为用来描述当某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。

1: 7种传播机制的介绍

public enum Propagation {
    REQUIRED(0),
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);

    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

1.1 REQUIRED (默认方式)

必须在事务中运行,如果当前存在事务则在当前事务中运行子方法,如果不存在事务则新建一个事务运行子方法。
Case1: 外层没有事务,内层事务是Required

@GetMapping("/exception/{name}/{type}")
    public void transSupportTest(@PathVariable("name") String name, @PathVariable("type") int type){
        this.houseService.insertAHouse();
        int i = 1/0;
        System.out.println("外层没有事务,内层事务是required,外层抛异常");
    }
@Transactional(propagation = Propagation.REQUIRED)
    public void insertAHouse(){
        MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
        this.houseMapper.insertAHouse(house);
        System.out.println("insertAHouse"+house.getHouse_id());
    }

运行结果:

house表中新增一条记录,user表没有新增记录
当外层没有事务时,内层是一个独立运行的事务,与外层没有关系,即外
层异常不会影响内层的运行

Case2: 外层有事务,内层事务是Required
修改外层insertUser代码设置为有事务

 @Transactional(propagation = Propagation.REQUIRED)
    public void insertUser(){
        this.houseService.insertAHouse();
        MUser user = new MUser("ayueyue", 90);
        this.userMapper.insertAuser(user);
        int i=1/0;
        System.out.println("insertAuser" + user.getCxy_id());
    }

运行结果:

house表和user表没有新增记录
内层与外层同属于一个事务,即:内层事务roll back,外层也要同时roll
back;如果外层事务roll back,内层也要同时roll back

1.2 SUPPORTS

如果当前方法已经存在事务中了则在事务中运行,如果不存在事务则在非事务环境下运行子方法
Case1: 外层没有事务,内层事务是SUPPORTS
外层代码:

public void insertUser(){
        this.houseService.insertAHouse();
        MUser user = new MUser("ayueyue", 90);
        int i=1/0;
        this.userMapper.insertAuser(user);
        System.out.println("insertAuser" + user.getCxy_id());
    }

内层代码:

 @Transactional(propagation = Propagation.SUPPORTS)
    public void insertAHouse(){
        MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
        this.houseMapper.insertAHouse(house);
        int i=1/0;
        this.houseMapper.insertAHouse(house);
        System.out.println("insertAHouse"+house.getHouse_id());
    }

运行结果:

house表中新增一条记录,user表没有新增记录
当外层没有事务时,内层在非事务环境下运行

Case2: 外层有事务,内层事务是SUPPORTS
外层代码:

 @Transactional
    public void transSupportTest(){
        this.houseService.insertAHouse();
        int i = 1/0;
        System.out.println("外层没有事务,内层事务是supports,外层抛异常");
    }

内层代码:

 @Transactional(propagation = Propagation.SUPPORTS)
    public void insertAHouse(){
        MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
        this.houseMapper.insertAHouse(house);
        System.out.println("insertAHouse"+house.getHouse_id());
    }

运行结果:

house表和user表没有新增记录
内层与外层同属于一个事务,即:内层事务roll back,外层也要同时roll
back;如果外层事务roll back,内层也要同时roll back

1.3 MANDATORY

必须在事务中运行,如果当前子方法不在事务中抛异常,如果在事务中则沿用当前事务
Case1: 外层没有事务,内层事务是MANDATORY
外层代码:

public void insertUser(){
        this.houseService.insertAHouse();
        MUser user = new MUser("ayueyue", 90);
        this.userMapper.insertAuser(user);
        System.out.println("insertAuser" + user.getCxy_id());
    }

内层代码:

 @Transactional(propagation = Propagation.MANDATORY)
    public void insertAHouse(){
        MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
        this.houseMapper.insertAHouse(house);
        System.out.println("insertAHouse"+house.getHouse_id());
    }

运行结果:


image.png

Case2: 外层有事务,内层事务是MANDATORY
外层代码:

   @Transactional
    public void insertUser(){
        this.houseService.insertAHouse();
        MUser user = new MUser("ayueyue", 90);
        this.userMapper.insertAuser(user);
        int i=1/0;
        System.out.println("insertAuser" + user.getCxy_id());
    }

内层代码:

 @Transactional(propagation = Propagation.MANDATORY)
    public void insertAHouse(){
        MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
        this.houseMapper.insertAHouse(house);
        System.out.println("insertAHouse"+house.getHouse_id());
    }

运行结果:

house表和user表没有新增记录
内层与外层同属于一个事务,即:内层事务roll back,外层也要同时roll
back;如果外层事务roll back,内层也要同时roll back

1.4 REQUIRES_NEW

如果当前方法已经存在事务中则挂起当前事务并新建一个事务运行子方法,如果不存在则新建一个事务运行
Case1: 外层方法和内层方法在同一个bean中
在userMapper中新增一个插入house表的接口,编写如下代码:

@Transactional
public void insertAUser(String name, int type){
    MUser user = new MUser(name, type);
    this.userMapper.insertAuser(user);
    System.out.println("insertAuser" + user.getCxy_id());
    this.insertAHouse(user.getCxy_id());
    int i = 1/0;
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertAHouse(int master){
    MHouse house = new MHouse("高新区中和街道"+master, "cxy"+master);
    this.userMapper.insertAHouse(house);
    System.out.println("insertAHouse"+house.getHouse_id());
}

运行结果:

house表和user表没有新增记录(内外层同时发生了roll back),即当二个事务在同一Bean中时,Requires_new没有重新起事务而是沿用了外层事务。
Case 2: 外层方法和内层方法在同一个bean中,外层没有事务

public void insertAUser(String name, int type){
    MUser user = new MUser(name, type);
    this.userMapper.insertAuser(user);
    int i = 1/0;
    System.out.println("insertAuser" + user.getCxy_id());
    this.insertAHouse(user.getCxy_id());
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertAHouse(int master){
    MHouse house = new MHouse("高新区中和街道"+master, "cxy"+master);
    this.userMapper.insertAHouse(house);
    System.out.println("insertAHouse"+house.getHouse_id());
}

运行结果:

house表新增一条记录,user表新增一条记录(insertAUser函数中的insertAHouse没有执行,因为是非事务环境)
即内层是一个独立运行的事务,和外层运行互不干扰。

Case 3: 外层方法和内层方法在不同的Bean中
在userservice中编写外层代码

      @Transactional
    public void insertUser(){
        this.houseService.insertAHouse();
        MUser user = new MUser("ayueyue", 90);
        this.userMapper.insertAuser(user);
        int i=1/0;
        System.out.println("insertAuser" + user.getCxy_id());
    }

在houseService中编写内层代码

   @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void insertAHouse(){
        MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
        this.houseMapper.insertAHouse(house);
        this.houseMapper.insertAHouse(house);
        System.out.println("insertAHouse"+house.getHouse_id());
    }

运行结果:

house表新增二条记录,user表没有新增记录
1): 即内层是一个独立运行的事务,和外层事务roll back不影响内层事务。
2): 当我们将外层的异常代码(int i=1/0)移到内层中,会发现内外层同时rollback 了,即内层出错了如果外层没有try catch,则内外层一起roll back
3): 去掉外层的事务,内层是一个独立运行的事务,外层以非事务方式运行

1.5 NOT_SUPPORTED

当外层没有事务时,该方法在非事务环境下运行
Case1: 外层方法和内层方法在同一个bean中
编写外层方法,设置事务

    @Transactional
    public void insertAUser(String name, int type){
        MUser user = new MUser(name, type);
        this.userMapper.insertAuser(user);
        insertAUserSameBean();
        System.out.println("insertAuser" + user.getCxy_id());
    }

编写内层方法,设置传播机制NOT_SUPPORTED

@Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void insertAUserSameBean(){
        MUser user = new MUser("sameBean", 999);
        this.userMapper.insertAuser(user);
        int i =1/0;
        //this.insertAUserInternal("sameBean", 999);
        System.out.println("insertAuser" + user.getCxy_id());
    }

运行结果:

house表和user表都没有新增记录(内层发生异常时把外层也roll back了)
即当内层方法和外层方法在同一个bean中时,NOT_SUPPORTE没有把外层的事务挂起,而是会沿用外层的事务
Case 2: 外层方法和内层方法不在同一个bean中
编写外层方法,设置事务

  @Transactional
    public void insertAUserInternal(){
        MUser user = new MUser("不在同一个bean", 234);
        this.userMapper.insertAuser(user);
        this.houseService.insertAHouse();
        System.out.println("insertAuser" + user.getCxy_id());
       // this.insertAUserException();
    }

在houseService类编写内层方法,设置传播机制NOT_SUPPORTED

@Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void insertAHouse(){
        MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
        this.houseMapper.insertAHouse(house);
        int i =1/0;
        this.houseMapper.insertAHouse(house);
        System.out.println("insertAHouse"+house.getHouse_id());
    }

运行结果:

house表新增一条记录,user表没有新增记录(内层是在非事务环境下运行的,而且发生异常时把外层也roll back了)
即当内层方法和外层方法不在同一个bean中时,内层方法在非事务环境下运行,当内层事务发生异常时,会将外层事务roll back.

1.6 NEVER

外层没有事务,非事务方式独立运行方法。当外层有事务分2种情况:
Case1: 外层方法和内层方法在同一个bean中
编写外层方法,设置事务

    @Transactional
    public void insertUser(){
        MUser user = new MUser("ayueyue", 90);
        this.userMapper.insertAuser(user);
        this.insertAUserException();
        System.out.println("insertAuser" + user.getCxy_id());
    }

编写内层方法,设置传播机制NEVER

@Transactional(propagation = Propagation.NEVER)
    public void insertAUserException(){
        MUser user = new MUser("ayueyue", 91);
        this.userMapper.insertAuser(user);
        System.out.println("insertAuser" + user.getCxy_id());
        int x = 1/0;
    }

运行结果:

user表没有新增记录(内层运行异常导致外层发生了roll back)
内层方法和外层同一个事务中运行。 内层出错,外层也rollback; 外层出错,内层也rollback
Case 2: 外层方法和内层方法不在同一个bean中
编写外层方法,设置事务

  @Transactional
    public void insertAUserInternal(){
        MUser user = new MUser("不在同一个bean", 234);
        this.userMapper.insertAuser(user);
        this.houseService.insertAHouse();
        System.out.println("insertAuser" + user.getCxy_id());
       // this.insertAUserException();
    }

在houseService类编写内层方法,设置传播机制NEVER

    @Transactional(propagation = Propagation.NEVER)
    public void insertAHouse(){
        MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
        this.houseMapper.insertAHouse(house);
        //int i =1/0;
        this.houseMapper.insertAHouse(house);
        System.out.println("insertAHouse"+house.getHouse_id());
    }

运行结果:

house表和user表都没有新增记录并且抛出异常


image.png

1.7 NESTED

如果当前已经在一个事务中了,则嵌套在已有的事务中作为一个子事务。如果当前没在事务中则开启一个事务。
外层没有事务:内层新建一个新的事务独立运行
外层有事务:内外层属于同一个事务,内层运行完后要等待外层一起提交。 外层出错roll back, 内层也roll back; 内层出错了如果外层有try catch,不影响外层执行只roll back内层; 如果外层没有try catch,则内外层一起roll back
case 1: 外层没有事务,内层事务NESTED:
外层代码:

 public void insertUser(){
        MUser user = new MUser("ayueyue", 90);
        this.userMapper.insertAuser(user);
        this.houseService.insertAHouse();
        System.out.println("insertAuser" + user.getCxy_id());
    }

内层代码:

    @Transactional(propagation = Propagation.NESTED)
    public void insertAHouse(){
        MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
        this.houseMapper.insertAHouse(house);
        int i =1/0;
        this.houseMapper.insertAHouse(house);
        System.out.println("insertAHouse"+house.getHouse_id());
    }

运行结果:

house表没有新增记录,user表新增记录,即当外层没有事务时,NESTED会单独起一个事务运行,与外层互不影响。
case 2:外层有事务,内层事务NESTED
外层代码

    @Transactional
    public void insertAUserInternal(){
        MUser user = new MUser("不在同一个bean", 234);
        this.userMapper.insertAuser(user);
        this.houseService.insertAHouse();
        int i = 1/0;
        System.out.println("insertAuser" + user.getCxy_id());
       // this.insertAUserException();
    }

内层代码

 @Transactional(propagation = Propagation.NESTED)
    public void insertAHouse(){
        MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
        this.houseMapper.insertAHouse(house);
        this.houseMapper.insertAHouse(house);
        System.out.println("insertAHouse"+house.getHouse_id());
    }

运行结果:

house表user表都没有新增记录,即当外层有事务时,NESTED嵌入到外层的事务中运行,同时提交,同时roll back.

2 事务的隔离机制

Spring在transaction.annotation包中规定了5种类型的隔离级别

public enum Isolation {
    //默认的隔离级别,使用数据库默认的事务隔离级别
    DEFAULT(-1),
    READ_UNCOMMITTED(1),
    READ_COMMITTED(2),
    REPEATABLE_READ(4),
    SERIALIZABLE(8);

    private final int value;

    private Isolation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

2.1 READ_UNCOMMITTED

未提交读,是事务隔离级别中最低的一种隔离方式。一个事务可以读取到其他事务没有提交数据,会导致脏读。

2.2 READ_COMMITTED

读写提交,一个事务只能读取其他事务已经提交的数据,可以避免数据脏读的情况。

2.3 REPEATABLE_READ

可重复读,一个事务只能在另外一个事务完全提交了数据后,才能再次读取数据。会导致幻读。

2.3 SERIALIZABLE

串行化,事务的最高隔离级别, 多个事务被处理为顺序执行。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容