Spring的7种事务传播机制

1.REQUIRED

REQUIRED(Spring默认的事务传播类型 required):如果当前没有事务,则自己新建一个事务,如果当前存在事务则加入这个事务。

当A调用B的时候:如果A中没有事务,B中有事务,那么B会新建一个事务;如果A中也有事务、B中也有事务,那么B会加入到A中去,变成一个事务,这时,要么都成功,要么都失败。

class ServiceA {
    @Autowired 
    ServiceB serviceB;
    
    @Transactional
    void a() {
        ....
        serviceB.b();
        ....
    }
}

class ServiceB {
    @Transactional
    void b() {
        ......
    }
}
  1. 线程执行到serviceA.a() 方法时,其实是执行的 代理serviceA对象的a方法。
  2. 执行代理serviceA对象的a方法
    2.1: 执行a方法的增强逻辑-> 事务增强器 (环绕增强)
    2.2: 事务增强器会做什么事? 提取事务标签属性
    2.3: 检查当前线程有没有绑定 conn 数据库连接 资源? 发现当前线程未绑定(TransactionSync...Manager#resources 是 ThreadLocal<Map<obj,obj>>,检查key:datasource 有没有数据)
    2.4: 因为未绑定conn资源,所以线程下一步就是 到 datasource.getConnection() 获取一个conn资源
    2.5: 因为新获取的conn资源的autocommit是true,所以这一步 修改 autocommit 为false,表示手动提交事务,这一步也表示 开启事务(修改conn其它 属性..)
    2.6: 绑定conn资源到 TransactionSync...Manager#resources,key:datasource
  3. 执行事务增强器后面的增强器..
  4. 最后一个advice调用 target的目标方法 a() 方法
    4.1: 假设target a方法 需要访问数据库 执行SQL 的话,程序需要获取一个 conn 资源,到哪拿? DataSourceUtils.getConnection(datasource) 这一步最终会拿到 事务增强器 前置增强逻辑 存放在 TransactionSync..Manager#resources 内的
    conn 资源
    4.2: 执行方法a逻辑...可能会执行一些 SQL 语句...
  5. 线程执行到这样一行代码:serviceB.b()
  6. serviceB 它是一个代理对象,因为它也使用了 @Transactional 注解了,Spring 会为它创建代理的。
  7. 执行代理serviceB对象的b方法
    7.1: 执行b方法的增强逻辑-> 事务增强器(环绕增强)
    7.2: 事务增强器会做什么事? 提取事务标签属性
    7.3: 检查当前线程有没有绑定 conn 数据库连接 资源?发现当前线程已经绑定了 conn 数据库连接资源了
    7.4: 检查事务注解属性,发现自己打的propagation == REQUIRED,所以继续共享 conn 数据库链接资源
  8. 执行事务增强器后面的增强器..
  9. 最后一个device调用 target (serviceB)的目标方法 b() 方法
    9.1: 假设target b方法 需要访问数据库 执行SQL 的话,程序需要获取一个 conn 资源,到哪拿? DataSourceUtils.getConnection(datasource) 这一步最终会拿到 代理serviceA对象存放在 TransactionSync..Manager#resources 内的
    conn 资源
    9.2: 执行方法b逻辑...可能会执行一些 SQL 语句...
  10. 线程继续执行 事务增强器 环绕增强的后置逻辑 (代理serviceB.b() 方法的 后置增强)
    10.1: 检查发现,serviceB.b() 事务并不是 当前 b方法开启的,所以 基本不做什么事情..
  11. 线程继续回到 目标 serviceA.a() 方法内,继续执行
    11.1: 执行方法a逻辑...可能会执行一些 SQL 语句...
  12. 线程继续回到 代理 serviceA.a() 方法内,继续执行
    12.1: 执行a方法的增强逻辑-> 事务增强器 (环绕增强-后置增强逻辑)
    12.2: 提交事务/回滚事务
    12.3: 恢复连接状态 (将conn的autocommit 设置回 true...等等)
    12.4: 清理工作(将绑定的conn资源从TransactionSync...Manager#resources移除)
    12.5: conn 连接关闭 (归还连接到datasource)

2.SUPPORTS

SUPPORTS:当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行。
如果A中有事务,则B方法的事务加入A事务中,成为一个事务(一起成功,一起失败),如果A中没有事务,那么B就以非事务方式运行(执行完直接提交)。

class ServiceA {
    @Autowired 
    ServiceB serviceB;
    
    @Transactional
    void a() {
        ....
        serviceB.b();
        ....
    }
}

class ServiceB {
    @Transactional(propagation = SUPPORTS)   
    void b() {
        ......
    }
}

逻辑和上面 完全一致。

class ServiceA {
    @Transactional(Propagation = SUPPORTS)
    void a() {
        ....

        ....
    }
}

线程在未绑定事务的情况下,去调用serviceA.a() 方法会发生什么呢?

  1. 线程执行到serviceA.a() 方法时,其实是执行的 代理serviceA对象的a方法。
  2. 执行代理serviceA对象的a方法
    2.1: 执行a方法的增强逻辑-> 事务增强器 (环绕增强)
    2.2: 事务增强器会做什么事? 提取事务标签属性
    2.3: 检查当前线程有没有绑定 conn 数据库连接 资源? 发现当前线程未绑定(TransactionSync...Manager#resources 是 ThreadLocal<Map<obj,obj>>,检查key:datasource 有没有数据)
    2.4: 啥也不用做..
  3. 执行事务增强器后面的增强器..
  4. 最后一个advice调用 target的目标方法 a() 方法
    4.1: 假设target a方法 需要访问数据库 执行SQL 的话,程序需要获取一个 conn 资源,到哪拿? DataSourceUtils.getConnection(datasource) ,因为事务增强器前置增强逻辑 并没有 向TransactionSync..Manager#resources 内绑定conn资源
    4.2: 因为 上一步未拿到 conn资源,所以 DataSourceUtils 通过 datasource.getConnection() 获取了一个全新的 conn 资源(注意:conn.autocommit == true,执行的每一条sql 都是一个 独立事务!!)
    4.3: 执行方法a逻辑...可能会执行一些 SQL 语句...
  5. 线程继续执行到代理serviceA对象的a方法 (事务增强器-后置增强逻辑)
    5.1: 检查发现 TrasactionSync..Manager#resources 并未绑定任何 conn 资源,所以 这一步啥也不做了...

3.MANDATORY

MANDATORY(mandatory:强制性的):当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
如果A中有事务,则B方法的事务加入A事务中,成为一个事务(一起成功,一起失败);如果A中没有事务,B中有事务,那么B就直接抛异常了,意思是B必须要支持回滚的事务中运行。

class ServiceA {
    @Autowired 
    ServiceB serviceB;
    
    @Transactional
    void a() {
        ....
        serviceB.b();
        ....
    }
}

class ServiceB {
    @Transactional(propagation = MANDATORY)
    void b() {
        ......
    }
}   

如果是这样的话,情况和 PROPAGATION_REQUIRED 案例分析 完全一致。

class ServiceA {
    @Transactional(Propagation = MANDATORY)
    void a() {
        ....

        ....
    }
}

线程在未绑定事务的情况下,去调用serviceA.a() 方法会发生什么呢?

  1. 线程执行到serviceA.a() 方法时,其实是执行的 代理serviceA对象的a方法。
  2. 执行代理serviceA对象的a方法
    2.1: 执行a方法的增强逻辑-> 事务增强器 (环绕增强)
    2.2: 事务增强器会做什么事? 提取事务标签属性
    2.3: 检查当前线程有没有绑定 conn 数据库连接 资源? 发现当前线程未绑定(TransactionSync...Manager#resources 是 ThreadLocal<Map<obj,obj>>,检查key:datasource 有没有数据)
    2.4: 直接抛出异常...

4.REQUIRES_NEW

REQUIRES_NEW:创建一个新事务,如果存在当前事务,则挂起该事务。
B会新建一个事务,A和B事务互不干扰,他们出现问题回滚的时候,也都只回滚自己的事务;A方法调用B方法;不管A方法有没有事务,B方法都新建一个自己的事务。

class ServiceA {
    @Autowired 
    ServiceB serviceB;
    
    @Transactional
    void a() {
        ....
        serviceB.b();
        ....
    }
}

class ServiceB {
    @Transactional(propagation = REQUIRES_NEW)
    void b() {
        ......
    }
}
  1. 线程执行到serviceA.a() 方法时,其实是执行的 代理serviceA对象的a方法。

  2. 执行代理serviceA对象的a方法
    2.1: 执行a方法的增强逻辑-> 事务增强器 (环绕增强)
    2.2: 事务增强器会做什么事? 提取事务标签属性
    2.3: 检查当前线程有没有绑定 conn 数据库连接 资源? 发现当前线程未绑定(TransactionSync...Manager#resources 是 ThreadLocal<Map<obj,obj>>,检查key:datasource 有没有数据)
    2.4: 因为未绑定conn资源,所以线程下一步就是 到 datasource.getConnection() 获取一个conn资源
    2.5: 因为新获取的conn资源的autocommit是true,所以这一步 修改 autocommit 为false,表示手动提交事务,这一步也表示 开启事务(修改conn其它 属性..)
    2.6: 绑定conn资源到 TransactionSync...Manager#resources,key:datasource

  3. 执行事务增强器后面的增强器..

  4. 最后一个advice调用 target的目标方法 a() 方法
    4.1: 假设target a方法 需要访问数据库 执行SQL 的话,程序需要获取一个 conn 资源,到哪拿? DataSourceUtils.getConnection(datasource) 这一步最终会拿到 事务增强器 前置增强逻辑 存放在 TransactionSync..Manager#resources 内的
    conn 资源
    4.2: 执行方法a逻辑...可能会执行一些 SQL 语句...

  5. 线程执行到这样一行代码:serviceB.b()

  6. serviceB 它是一个代理对象,因为它也使用了 @Transactional 注解了,Spring 会为它创建代理的。

  7. 执行代理serviceB对象的b方法
    7.1: 执行b方法的增强逻辑-> 事务增强器(环绕增强)
    7.2: 事务增强器会做什么事? 提取事务标签属性
    7.3: 检查发现当前线程已经绑定了conn资源(并且手动开启了事务..),又发现 当前方法的 传播行为:REQUIRES_NEW ,需要开启一个新的事务..
    7.4: 将已经绑定的conn资源 保存到 suspand 变量内
    7.5: 因为 REQUIRES_NEW 不会和上层共享同一个事务,所以这一步 又到 datasource.getConnection() 获取了一个全新的 conn 数据库连接资源
    7.6: 因为新获取的conn资源的autocommit是true,所以这一步 修改 autocommit 为false,表示手动提交事务,这一步也表示 开启事务(修改conn其它 属性..)
    7.7: 绑定conn资源到 TransactionSync...Manager#resources,key:datasource

  8. 执行事务增强器后面的增强器..

  9. 最后一个advice调用 target (serviceB)的目标方法 b() 方法
    9.1: 假设target b方法 需要访问数据库 执行SQL 的话,程序需要获取一个 conn 资源,到哪拿? DataSourceUtils.getConnection(datasource) 这一步最终会拿到 事务增强器 前置增强逻辑 存放在 TransactionSync..Manager#resources 内的
    conn 资源
    9.2: 执行方法a逻辑...可能会执行一些 SQL 语句...

  10. 线程继续执行 事务增强器 环绕增强的后置逻辑 (代理serviceB.b() 方法的 后置增强)
    10.1: 检查发现,serviceB.b() 事务是 b方法开启的,所以 需要做一些事情了
    10.1: 执行b方法的增强逻辑-> 事务增强器 (环绕增强-后置增强逻辑)
    10.2: 提交事务/回滚事务
    10.3: 恢复连接状态 (将conn的autocommit 设置回 true...等等)
    10.4: 清理工作(将绑定的conn资源从TransactionSync...Manager#resources移除)
    10.5: conn 连接关闭 (归还连接到datasource)
    10.6: 检查suspand 发现 该变量有值,需要执行 恢复现场的工作 resume()

  11. 恢复现场
    11.1: 将suspand 挂起的 conn 资源再次 绑定到 TransactionSync...Manager#resources 内,方便 serviceA 继续使用它的conn资源 (它自己的事务)

  12. 线程继续回到 serviceA.a() 方法内
    12.1: 继续执行一些sql ...注意 这里它使用的 conn 是 serviceA 申请的 conn

  13. 线程继续执行 事务增强器 环绕增强的后置逻辑 (代理serviceA.a() 方法的 后置增强)
    10.1: 检查发现,serviceA.a() 事务是 a方法开启的,所以 需要做一些事情了
    10.1: 执行a方法的增强逻辑-> 事务增强器 (环绕增强-后置增强逻辑)
    10.2: 提交事务/回滚事务
    10.3: 恢复连接状态 (将conn的autocommit 设置回 true...等等)
    10.4: 清理工作(将绑定的conn资源从TransactionSync...Manager#resources移除)
    10.5: conn 连接关闭 (归还连接到datasource)

5.NOT_SUPPORTED

NOT_SUPPORTED: 以非事务方式执行,如果当前存在事务,则挂起当前事务
被调用者B会以非事务方式运行(直接提交),如果当前有事务,也就是A中有事务,A会被挂起(不执行,等待B执行完,返回);A和B出现异常需要回滚,互不影响

6.NEVER

NEVER: 如果当前没有事务存在,就以非事务方式执行;如果有,就抛出异常。就是B从不以事务方式运行
A中不能有事务,如果没有,B就以非事务方式执行,如果A存在事务,那么直接抛异常

7.NESTED

NESTED: 嵌套事务:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)
如果A中没有事务,那么B创建一个事务执行,如果A中也有事务,那么B会会把事务嵌套在里面。

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

推荐阅读更多精彩内容