在 spring 中一共定义了六种事务传播属性, 如果你觉得看起来不够直观, 那么我来转贴一个满大街都有的翻译
引用
l          PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
l          PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
l          PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
l          PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
l          PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
l          PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
l          PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。 它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)

在我所见过的误解中, 最常见的是下面这种:
引用
假如有两个业务接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一个方法实现如下
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
// 调用 ServiceB 的方法
ServiceB.methodB();
}
那么如果 ServiceB 的 methodB 如果配置了事务, 就必须配置为 PROPAGATION_NESTED
这种想法可能害了不少人, 认为 Service 之间应该避免互相调用, 其实根本不用担心这点,PROPAGATION_REQUIRED 已经说得很明白,如果当前线程中已经存在事务, 方法调用会加入此事务, 果当前没有事务,就新建一个事务, 所以 ServiceB#methodB() 的事务只要遵循最普通的规则配置为 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我们称之为内部事务, 为下文打下基础) 抛了异常, 那么 ServiceA#methodA(我们称之为外部事务) 如果没有特殊配置此异常时事务提交 (即 +MyCheckedException的用法), 那么整个事务是一定要 rollback 的, 什么 Service 只能调 Dao 之类的言论纯属无稽之谈, spring 只负责配置了事务属性方法的拦截, 它怎么知道你这个方法是在 Service 还是 Dao 里 ?

也就是说, 最容易弄混淆的其实是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED, 那么这两种方式又有何区别呢? 我简单的翻译一下 Juergen Hoeller 的话 :
PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 committed 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.
由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back.
那么外部事务如何利用嵌套事务的 savepoint 特性呢, 我们用代码来说话
代码
ServiceA {
    /**  
     * 事务属性配置为 PROPAGATION_REQUIRED  
     */  
    void methodA() {   
        ServiceB.methodB();   
    }   
}   
  
ServiceB {
    /**  
     * 事务属性配置为 PROPAGATION_REQUIRES_NEW  
     */    
    void methodB() {   
    }  
}      
这种情况下, 因为 ServiceB#methodB 的事务属性为 PROPAGATION_REQUIRES_NEW, 所以两者不会发生任何关系, ServiceA#methodA 和 ServiceB#methodB 不会因为对方的执行情况而影响事务的结果, 因为它们根本就是两个事务, 在 ServiceB#methodB 执行时 ServiceA#methodA 的事务已经挂起了 (关于事务挂起的内容已经超出了本文的讨论范围, 有时间我会再写一些挂起的文章) .
那么 PROPAGATION_NESTED 又是怎么回事呢? 继续看代码
代码
ServiceA {
    /**  
     * 事务属性配置为 PROPAGATION_REQUIRED  
     */  
    void methodA() {   
        ServiceB.methodB();   
    }  
}   
  
ServiceB {
    /**  
     * 事务属性配置为 PROPAGATION_NESTED  
     */    
    void methodB() {   
    }   
}      
现在的情况就变得比较复杂了, ServiceB#methodB 的事务属性被配置为 PROPAGATION_NESTED, 此时两者之间又将如何协作呢? 从 Juergen Hoeller 的原话中我们可以找到答案, ServiceB#methodB 如果 rollback, 那么内部事务(即 ServiceB#methodB) 将回滚到它执行前的 SavePoint(注意, 这是本文中第一次提到它, 嵌套事务中最核心的概念), 而外部事务(即 ServiceA#methodA) 可以有以下两种处理方式:
1. 改写 ServiceA 如下
代码
ServiceA {   
       
    /**  
     * 事务属性配置为 PROPAGATION_REQUIRED  
     */  
    void methodA() {   
        try {   
            ServiceB.methodB();   
        } catch (SomeException) {   
            // 执行其他业务, 如 ServiceC.methodC();   
        }   
    }   
  
}   
  
这种方式也是嵌套事务最有价值的地方, 它起到了分支执行的效果, 如果 ServiceB.methodB 失败, 那么执行 ServiceC.methodC(), 而 ServiceB.methodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都没有办法做到这一点. (题外话 : 看到这种代码, 似乎似曾相识, 想起了 prototype.js 中的 Try 函数 )
2. 代码不做任何修改, 那么如果内部事务(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滚到它执行之前的 SavePoint(在任何情况下都会如此), 外部事务(即 ServiceA#methodA) 将根据具体的配置决定自己是 commit 还是 rollback (+MyCheckedException).
上面大致讲述了嵌套事务的使用场景, 下面我们来看如何在 spring 中使用 PROPAGATION_NESTED, 首先来看AbstractPlatformTransactionManager
代码
/**  
 * Create a TransactionStatus for an existing transaction.  
 */  
private TransactionStatus handleExistingTransaction(   
        TransactionDefinition definition, Object transaction, boolean debugEnabled)   
        throws TransactionException {   
  
   ... 省略   
  
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {   
        if (!isNestedTransactionAllowed()) {   
            throw new NestedTransactionNotSupportedException(   
                    "Transaction manager does not allow nested transactions by default - " +   
                    "specify 'nestedTransactionAllowed' property with value 'true'");   
        }   
        if (debugEnabled) {   
            logger.debug("Creating nested transaction with name [" + definition.getName() + "]");   
        }   
        if (useSavepointForNestedTransaction()) {   
            // Create savepoint within existing Spring-managed transaction,   
            // through the SavepointManager API implemented by TransactionStatus.   
            // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.   
            DefaultTransactionStatus status =   
                    newTransactionStatus(definition, transaction, false, false, debugEnabled, null);   
            status.createAndHoldSavepoint();   
            return status;   
        }   
        else {   
            // nested transaction through nested begin and commit/rollback calls.   
            // Usually only for JTA: Spring synchronization might get activated here   
            // in case of a pre-existing JTA transaction.   
            doBegin(transaction, definition);   
            boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);   
            return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);   
        }   
    }   
}   
 
一目了然
1. 我们要设置 transactionManager 的 nestedTransactionAllowed 属性为 true, 注意, 此属性默认为 false!!!再看 AbstractTransactionStatus#createAndHoldSavepoint() 方法
代码
/**  
 * Create a savepoint and hold it for the transaction.  
 * @throws org.springframework.transaction.NestedTransactionNotSupportedException  
 * if the underlying transaction does not support savepoints  
 */  
public void createAndHoldSavepoint() throws TransactionException {   
    setSavepoint(getSavepointManager().createSavepoint());   
}   
可以看到 Savepoint 是 SavepointManager.createSavepoint 实现的, 再看 SavepointManager 的层次结构, 发现其 Template 实现是 JdbcTransactionObjectSupport, 常用的 DatasourceTransactionManager, HibernateTransactionManager 中的 TransactonObject 都是它的子类 :
 
1.      JdbcTransactionObjectSupport 告诉我们必须要满足两个条件才能 createSavepoint :
2.      java.sql.Savepoint 必须存在, 即 jdk 版本要 1.4+
3.      Connection.getMetaData().supportsSavepoints() 必须为 true, 即 jdbc drive 必须支持 JDBC 3.0
确保以上条件都满足后, 你就可以尝试使用 PROPAGATION_NESTED 了. (全文完)

spring 事务说明相关推荐

  1. (转)面试必备技能:JDK动态代理给Spring事务埋下的坑!

    一.场景分析 最近做项目遇到了一个很奇怪的问题,大致的业务场景是这样的:我们首先设定两个事务,事务parent和事务child,在Controller里边同时调用这两个方法,示例代码如下: 1.场景A ...

  2. spring的事务隔离_再深一点:面试工作两不误,源码级理解Spring事务

    原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处. Spring有5种隔离级别,7种传播行为.这是面试常问的内容,也是代码中经常碰到的知识点.这些知识枯燥而且乏味,其中有些非 ...

  3. spring上下文是什么意思_Java程序员只会CRUD连Spring事务传播机制都不懂?

    AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东西,面试完就 ...

  4. Spring事务管理的底层逻辑—源码解析

    本文代码为spring 5.1.2 spring是如何控制事务的提交和回滚 加上@Transactional注解之后,Spring可以启到事务控制的功能了,再正式执行方法前它会做一些操作,我们来看看 ...

  5. springboot 事务手动回滚_来,讲讲Spring事务有哪些坑?

    来自公众号:孤独烟 引言 今天,我们接上文<面试官:谈谈你对mysql事务的认识>的内容,来讲spring中和事务有关的考题! 因为事务这块,面试的出现几率很高.而大家工作中CRUD的比较 ...

  6. Spring事务管理 与 SpringAOP

    1,Spring事务的核心接口 Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略.  ...

  7. Spring 事务失效?看这篇文章就够了!

    欢迎关注方志朋的博客,回复"666"获面试宝典 用 Spring 的 @Transactional 注解控制事务有哪些不生效的场景? 不知道小伙伴们有没有这样的经历,在自己开心的编 ...

  8. 面试官: 讲讲 Spring 事务有哪些坑?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 引言 今天,我们来讲 Spring 中和事务有关的考题. ...

  9. 一文带你看懂Spring事务!

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 前言 Spring事务管理我相信大家都用得很多,但可能仅仅局限于一个@Transactiona ...

  10. java 封装事务_Spring之路(36)–事务很重要吗?Spring为何要封装事务?Spring事务有陷阱吗?...

    可以不使用事务吗 真的遇到过开发企业应用,但是不知道啥是事务的朋友. 好吧,我始终认为抛开性能.界面.易用性等方面不说,正确性应该是一个项目最基础的要求了. 所以,凡是企业应用,正儿八经有客户的系统, ...

最新文章

  1. 数据科学家必须要掌握的5种聚类算法
  2. const,readonly 这些你真的懂吗? 也许会被面试到哦。。。
  3. 文档标题:WinNTWin2K下实现进程的完全隐藏
  4. 字符串作为freemarker模板的简单实现例子
  5. MyEclipse结合Git
  6. NeHe教程Qt实现——lesson03
  7. 提高SQL语句的性能
  8. 【干货】深入B端SaaS产品设计核心理念
  9. 【树链剖分】染色(luogu 2486/金牌导航 树链剖分-3)
  10. Java中array、List、Set互相转换
  11. 你在寻觅冬季唯美的海报设计素材么?
  12. 360,选择顶你还是拍你啊?
  13. mysql 视图调用存储过程,是否可以在视图中调用存储过程?
  14. Java操作DB2 XML数据实践
  15. Linux系统之下的基本gdb调试
  16. Alibaba代码检查工具插件
  17. ubuntu code::blocks 汉化(附汉化包)
  18. 编译的html帮助文件(.chm)打不开,chm文件无法打开怎么办
  19. Windows创建用户定义的服务(srvany.exe和instsrv.exe )
  20. php 字符相似度比较,php 比较两个字符串的相似度

热门文章

  1. python slice函数画高维图_Python 绘制 3 维以上的高维图
  2. 设置行内元素宽高和背景色后,行内元素文本不水平垂直居中解决方案
  3. python热部署_定时任务-Quartz(热部署、冷部署)
  4. linux下mysql的备份_Linux下MySQL的备份与还原
  5. android编程读取sd卡txt文件,如何读取SD卡中的txt文件?
  6. 达摩院文档级关系抽取新SOTA和零样本关系抽取新任务
  7. ICLR 2021 | 美团、上交大等:鲁棒的可微分神经网络搜索DARTS-
  8. 总奖金近9万!视频超分辨率大赛等你来战!
  9. 微信好友特征数据分析及可视化
  10. 牛客网 短最优升级路径 【Dijkstra算法】+【路径记录】