Spring事务传播属性介绍(一).required 和 reuqires_new

摘要:
修改ServiceA的callB方法以直接查看输出日志:查看日志。记录已添加但已回滚,数据库中没有记录。

Mandatory、Never、Not_Support传播属性分析传送门:https://www.cnblogs.com/lvbinbin2yujie/p/10260030.html

  Nested传播属性分析传送门:https://www.cnblogs.com/lvbinbin2yujie/p/10260066.html

  我的Spring事务传播属性介绍比较传送门:https://files.cnblogs.com/files/lvbinbin2yujie/Spring_Tx_Note.rar

最近查看了Spring事务源码,是4.2.x的版本还是4.3.x的版本,简单了解了一些事务的概念,介绍下我对Spring事务源码的分析.

Spring一共七种事务传播属性,本文先来作为开篇介绍。

  REQUIRED事务,Spring Transactional注解默认的事务,需要该方法在有事务情况下运行,如果当前没有事务就新建一个事物;

  REQUIRES_NEW事务,当前方法运行没有事务,新建一个事物,当前方法有事务将当前事务挂起,新事物执行完毕再恢复原有事务;

这里提一点.,以前不明白什么是同一事务核心是什么,后来看到有位仁兄介绍,同一事务的事务信息、事务状态对象不同,但是底层是同一个Connection对象;这点我深以为.

实验说明环节

包结构:          

 Spring事务传播属性介绍(一).required 和 reuqires_new第1张

Spring配置文件:  (简单介绍下, 定义了一个数据源、DataSourceTransactionManager;此外tx:annotation-driven作用是用来启用Transactional注解的)

     Spring事务传播属性介绍(一).required 和 reuqires_new第2张

ServiceA.java文件

 Spring事务传播属性介绍(一).required 和 reuqires_new第3张

ServiceB.java文件

 Spring事务传播属性介绍(一).required 和 reuqires_new第4张

测试Main方法:

 Spring事务传播属性介绍(一).required 和 reuqires_new第5张

PROPAGATION_REQUIRED

说明: 默认的事务级别, 需要事务;如果当前没有事务,则创建新的事务;如果有事务呢,就加入当前的事务;

如果所有的Transactional标签都是默认的,REQUIRED时,方法里的提交、回滚都是一起的,要么所有都提交,要么所有都回滚;一荣俱荣,一损俱损;

查看源码时候打印事务的日志:

 Spring事务传播属性介绍(一).required 和 reuqires_new第6张

查看输出结果:  可以看到 确实提交了,并且加入了之前的事务,加入之前事务就是共用的一个Connection对象;

 Spring事务传播属性介绍(一).required 和 reuqires_new第7张

 情景二:  修改ServiceB的addUser方法  来模拟调用其他业务方法时候执行抛出运行时异常;  (同时将数据库之前测试结果 删除,便于观察)

 Spring事务传播属性介绍(一).required 和 reuqires_new第8张

   先查看结果:  艾尼路记录添加进来又回退了, 查看日志:

 Spring事务传播属性介绍(一).required 和 reuqires_new第9张

说明:  addUser抛出类型为RuntimeException类型的异常,callB捕获该异常之后,发现addUser在事物callB中,于是将事务状态DefaultTransactionStatus对戏中的事务对象DataSourceTransactionObject中的ConnectionHolder对象,即持有底层Connection的对象,将ConnectionHolder标记为rollbackOnly为true,然后将异常抛给调用addUser的callB方法;

在callB调用时根据Spring事务回滚规则,决定回滚操作;因为在情景二同一个事务中ConnectionHolder中持有的Connection对象是同一个,所以callB方法整个回滚了;

 

 

情景三: callB方法中手动try-catch来捕获异常会发生什么呢?

         修改ServiceA的callB方法(为了便于观察,不输出异常,只是简单打印)

 Spring事务传播属性介绍(一).required 和 reuqires_new第10张

直接查看输出日志(最后一行抛出的异常排版原因未截全): 查看日志,记录确实添加了但是又回滚过了,数据库里没有记录。

异常信息:

Exception in thread "main" org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

 Spring事务传播属性介绍(一).required 和 reuqires_new第11张

 

说明: callB作为事务的开始,addUser作为事务的一部分,addUser抛出异常以后将ConnectionHolder对象的rollbackOnly设置为true,标记为需要回滚,但是执行完callB方法,没有抛出异常,就认为应该正常提交; 提交之前会校验两次,有一次是 Global transaction is marked as rollback-only but transactional code requested commit;

意思是全局事务需要回滚操作,但是事务代码现在要提交,这时候Spring还是会回滚;(通俗点就是addUser方法需要回滚,但是callB方法没有发生预料之外的异常,因为异常自己手动捕获,这时候Spring还是会回滚);

         callB和ServiceB的事务信息对象TransactionInfo是不一样的,其属性事务状态TransactionStatus也不是同一个对象,TransactionStatus的底层事务对象transaction也不是同一个对象,transaction持有ConnectionHolder对象的Connection确实同一个,这样就保证了可以使用Connection对象来保持事务的一致性,一起提交、一起回滚; 保证两个Connection是同一个对象的是ThreadLocal,TransactionSynchronizationManager的resources里存放了着;

 

总结:  REQUIRED不建议手动捕获异常,会破坏Spring的事务规则;try-catch需要结合传播类型,再决定使用与否;

PROPAGATION_REQUIRES_NEW

说明: 创建新的事务并执行,如果当前有事务,那将当前事务挂起,新建一个事物;

 

给ServiceA类callB方法修改下:

Spring事务传播属性介绍(一).required 和 reuqires_new第12张

ServiceB类添加方法addUserFail,事务属性设置为REQUIRE_NEW

 Spring事务传播属性介绍(一).required 和 reuqires_new第13张

正常情况下查看输出日志:

可以发现,当进入REQUIRED_NEW事务里的方法时,挂起了原来的事务,事务执行完毕恢复了事务;并且外层事务和addUserFail事务是分别提交的;

 Spring事务传播属性介绍(一).required 和 reuqires_new第14张

 

情景三.

ServiceA方法:

 Spring事务传播属性介绍(一).required 和 reuqires_new第15张

ServiceB方法:

Spring事务传播属性介绍(一).required 和 reuqires_new第16张

测试类方法:

 Spring事务传播属性介绍(一).required 和 reuqires_new第17张

 

说明:callB2方法调用了ServiceB的两个事务方法,ddUserSuccess方法是REQUIRED事务,addUserFail方法是REQUIRED_NEW事务,按照之前分析的,REQUIRED_NEW的方法是一个新的事务,那我抛出异常自己就会回滚,不应该干扰到callB2方法的回滚;

执行结果: addUserSuccess方法被带着一起回滚了,即外层事务也被带着一起回滚了;

 

查看源码发现: addUserFail的确新建了事务,然后抛出异常之后,着手回滚,回滚完成后将异常throw了,异常被throw那就会丢给调用addUserFail的地方,没错,丢到了callB2方法里,那callB2也着火了,发生异常,这时候callB2的事务也被认定为执行失败应当回滚,那callB2的事务就开始回滚,callB2内部事务addUserSuccess回滚, 所以一条记录都没有写进去.

(想了下,REQUIRED_NEW事务的方法就在调用他的地方手动捕获异常,不让异常向上传递了,这样就能达到目的,且不会像REQUIRED一样报异常)

 

简单画了如下例子,外层事务为REQUIRED类型;外层事务有两个方法,A、B;Spring默认回滚规则为RuntimeException或Error类型,下面例子抛出异常也是RumtimeException或Error类型,且没有手动try-catch捕获异常;

 Spring事务传播属性介绍(一).required 和 reuqires_new第18张

 

免责声明:文章转载自《Spring事务传播属性介绍(一).required 和 reuqires_new》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇网易新闻RSS阅读器mysql 禁用查询缓存 query cache下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Hbase事务

原文:http://hbasefly.com/2017/07/26/transaction-2/ 1、关于hbase事务 HBase目前只支持行级事务; 可以保证行级数据的原子性、一致性、隔离性以及持久性,即通常所说的ACID特性。 为了实现事务特性,HBase采用了各种并发控制策略,包括各种锁机制、MVCC机制等。 2、hbase事务原子性 hbas...

KUDU 介绍

前言 近两年,KUDU 在大数据平台的应用越来越广泛。在阿里、小米、网易等公司的大数据架构中,KUDU 都有着不可替代的地位。本文通过分析 KUDU 的设计, 试图解释为什么 KUDU 会被广泛应用于大数据领域,因为还没有研究过 KUDU 的代码,下面的介绍是根据 KUDU 的论文和网上的一些资料学习自己理解所得,如有不实之处,劳请指正。 背景 在 KU...

sql 事务和回滚

(1) set ANSI_NULLS ON --见图1 set QUOTED_IDENTIFIER ON go ALTER proc [dbo].[procname] as begin begin transaction DECLARE @myerror INT set @myerror=0 delete from...

postgresql-锁相关

postgresql-并发控制 目录 postgresql-并发控制 显示锁定 表级锁八种表级锁的强度 行级锁四种行级锁的强度 页级锁 咨询锁 死锁 postgres中关于锁等待参数 总结 显示锁定 PostgreSQL提供了多种锁模式用于控制表中数据的并发访问。这些模式可以用于在mvcc无法给出期望行为的场合。同样,大多数Postg...

数据库并发处理

为什么要有锁? 我们都是知道,数据库中锁的设计是解决多用户同时访问共享资源时的并发问题。在访问共享资源时,锁定义了用户访问的规则。根据加锁的范围,MySQL 中的锁可大致分成全局锁,表级锁和行锁三类。在本篇文章中,会依次介绍三种类型的锁。在阅读本篇文章后,应该掌握如下的内容: 为什么要在备份时使用全局锁? 为什么推荐使用 InnoDB 作为引擎进行备份?...

Java开启事务操作数据库

一、事务的四大特性(ACID) 1、原子性(atomicity):组成事务的语句形成了一个逻辑单元,不能只执行一部分; 2、一致性(consistency):在事务处理执行前后,数据库与理论值是一致的(数据库完整性约束); 3、隔离性(isolcation):一个事务处理和另一个事务处理相互间互不影响; 4、持续性(durability):事务处理的效果能...