Spring事务传播机制Word文档下载推荐.docx

上传人:b****3 文档编号:17056727 上传时间:2022-11-28 格式:DOCX 页数:9 大小:22.34KB
下载 相关 举报
Spring事务传播机制Word文档下载推荐.docx_第1页
第1页 / 共9页
Spring事务传播机制Word文档下载推荐.docx_第2页
第2页 / 共9页
Spring事务传播机制Word文档下载推荐.docx_第3页
第3页 / 共9页
Spring事务传播机制Word文档下载推荐.docx_第4页
第4页 / 共9页
Spring事务传播机制Word文档下载推荐.docx_第5页
第5页 / 共9页
点击查看更多>>
下载资源
资源描述

Spring事务传播机制Word文档下载推荐.docx

《Spring事务传播机制Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《Spring事务传播机制Word文档下载推荐.docx(9页珍藏版)》请在冰豆网上搜索。

Spring事务传播机制Word文档下载推荐.docx

基于元数据的Spring声明式事务:

Isolation属性一共支持五种事务设置,具体介绍如下:

DEFAULT

使用数据库设置的隔离级别(默认),由DBA默认的设置来决定隔离级别,即由底层数据库自动判断应该使用什么隔离级别

READ_UNCOMMITTED

可以读取未提交数据,可能出现脏读、不可重复读、幻读(隔离级别最低,并发性能高,效率最高)

READ_COMMITTED

只能读取其它事务已提交数据,可以防止脏读,可能出现不可重复读、幻读问题(锁定正在读取的行)

REPEATABLE_READ

读取的数据被添加锁(锁定所读取的所有行),防止其它事务修改此数据,可以防止不可重复读、脏读,可能出现幻读

SERIALIZABLE

排队操作,对整个表添加锁,一个事务在操作数据时,另一个事务等待事务操作完成后才能操作这个表。

无脏读、不可重复读以及幻读的问题(最安全但效率最低)

不可重复读的重点是修改 

:

同样的条件, 

你读取过的数据, 

再次读取出来发现值不一样了 

幻读的重点在于新增或者删除

第1次和第2次读出来的记录数不一样

Spring在TransactionDefinition接口中定义这些属性

在TransactionDefinition接口中定义了五个不同的事务隔离级别

ISOLATION_DEFAULT 

这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应。

ISOLATION_READ_UNCOMMITTED 

这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。

这种隔离级别会产生脏读,不可重复读和幻像读。

ISOLATION_READ_COMMITTED 

保证一个事务修改的数据提交后才能被另外一个事务读取。

另外一个事务不能读取该事务未提交的数据。

这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。

ISOLATION_REPEATABLE_READ 

这种事务隔离级别可以防止脏读,不可重复读。

但是可能出现幻像读。

它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。

ISOLATION_SERIALIZABLE 

这是花费最高代价但是最可靠的事务隔离级别。

事务被处理为顺序执行。

除了防止脏读,不可重复读外,还避免了幻像读。

4.七种事务传播行为

propagation控制事务传播行为:

当一个具有事务控制的方法被另一个有事务控制的方法调用后,需要如何管理事务(新建事务?

在事务中执行?

把事务挂起?

报异常?

在TransactionDefinition接口中定义了七个事务传播行为。

PROPAGATION_REQUIRED(默认值):

如果当前存在一个事务,则支持当前事务(在事务中执行)。

如果当前没有事务,则开启一个新的事务(新建一个事务)。

PROPAGATION_SUPPORTS:

如果当前存在一个事务,支持当前事务。

如果没有事务,则在非事务状态下执行。

但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有些许不同。

PROPAGATION_MANDATORY:

必须在事务内部执行,如果当前已经存在一个事务,支持当前事务(在当前事务中执行)。

如果没有一个活动的事务,则抛出异常(报错)。

PROPAGATION_REQUIRES_NEW:

必须在事务中执行,如果当前没有事务,则新建一个事务(注:

当前事务与新建的事务是毫不相干的两个事务);

如果当前存在事务,则将当前事务挂起(新建的事务产生异常进行回滚时,不会导致挂起的事务也被回滚)。

PROPAGATION_NOT_SUPPORTED:

必须在非事务状态下执行,如果当前没有事务,则正常执行;

如果当前存在事务,则把当前事务挂起。

PROPAGATION_NEVER:

总是非事务地执行(必须在非事务状态下执行)。

如果当前没有事务,则正常执行;

如果当前存在一个活动事务,则抛出异常(报错)。

PROPAGATION_NESTED:

必须在事务状态下执行。

如果当前没有事务,则新建事务(即按TransactionDefinition.PROPAGATION_REQUIRED属性执行);

如果当前存在一个活动的事务,则创建一个嵌套事务。

事务是逻辑处理原子性的保证手段,通过使用事务控制,可以极大的避免出现逻辑处理失败导致的脏数据等问题。

事务最重要的两个特性,是事务的传播级别和数据隔离级别。

传播级别定义的是事务的控制范围,事务隔离级别定义的是事务在数据库读写方面的控制范围。

事务的7种传播级别:

1)PROPAGATION_REQUIRED,默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。

所以这个级别通常能满足处理大多数的业务场景。

2)PROPAGATION_SUPPORTS,从字面意思就知道,supports,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。

所以说,并非所有的包在transactionTemplate.execute中的代码都会有事务支持。

这个通常是用来处理那些并非原子性的非核心业务逻辑操作。

应用场景较少。

3)PROPAGATION_MANDATORY,该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!

配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。

比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。

4)PROPAGATION_REQUIRES_NEW,从字面即可知道,new,每次都要一个新事务,该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。

这是一个很有用的传播级别,举一个应用场景:

现在有一个发送100个红包的操作,在发送之前,要做一些系统的初始化、验证、数据记录操作,然后发送100封红包,然后再记录发送日志,发送日志要求100%的准确,如果日志不准确,那么整个父事务逻辑需要回滚。

怎么处理整个业务需求呢?

就是通过这个PROPAGATION_REQUIRES_NEW级别的事务传播控制就可以完成。

发送红包的子事务不会直接影响到父事务的提交和回滚。

5)PROPAGATION_NOT_SUPPORTED,这个也可以从字面得知,notsupported,不支持,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。

这个级别有什么好处?

可以帮助你将事务极可能的缩小。

我们知道一个事务越大,它存在的风险也就越多。

所以在处理事务的过程中,要保证尽可能的缩小范围。

比如一段代码,是每次逻辑操作都必须调用的,比如循环1000次的某个非核心业务逻辑操作。

这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况。

所以这个事务这个级别的传播级别就派上用场了。

用当前级别的事务模板抱起来就可以了。

6)PROPAGATION_NEVER,该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!

这个级别上辈子跟事务有仇。

7)PROPAGATION_NESTED,字面也可知道,nested,嵌套级别事务。

该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

那么什么是嵌套事务呢?

很多人都不理解,我看过一些博客,都是有些理解偏差。

嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫savepoint,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。

重点就在于那个savepoint。

看几个问题就明了了:

如果子事务回滚,会发生什么?

父事务会回滚到进入子事务前建立的savepoint,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。

如果父事务回滚,会发生什么?

父事务回滚,子事务也会跟着回滚!

为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。

那么:

事务的提交,是什么情况?

是父事务先提交,然后子事务提交,还是子事务先提交,父事务再提交?

答案是第二种情况,还是那句话,子事务是父事务的一部分,由父事务统一提交。

现在你再体会一下这个”嵌套“,是不是有那么点意思?

以上是事务的7个传播级别,在日常应用中,通常可以满足各种业务需求,但是除了传播级别,在读取数据库的过程中,如果两个事务并发执行,那么彼此之间的数据是如何影响的呢?

这就需要了解一下事务的另一个特性:

数据隔离级别

5.四种数据隔离级别

在多线程或并发访问下如何保证访问到的数据具有完整性的(即数据是对的无错乱的)。

1、Serializable:

最严格的级别,事务串行执行,资源消耗最大;

2、REPEATABLEREAD:

保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。

避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。

3、READCOMMITTED:

大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。

该级别适用于大多数系统。

4、ReadUncommitted:

保证了读取过程中不会读取到非法数据。

上面的解释其实每个定义都有一些拗口,其中涉及到几个术语:

脏读、不可重复读、幻读。

这里解释一下:

脏读

一个事务(A)读取到另一个事务(B)中未提交的数据,另一个事务(B)中的数据可能发生了改变,此时事务A读取的数据可能和数据库中的数据是不一致的,此时就认为数据是脏数据,读取脏数据的过程叫脏读。

所谓的脏读,其实就是读到了别的事务回滚前的脏数据。

比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。

不可重复读

⏹主要针对的是某行数据(或行中某列)

⏹主要针对的操作是修改操作

⏹两次读取在同一个事务内

⏹当事务A第一次读取事务后,事务B对事务A读取的数据进行了修改,事务A中再次读取的数据和之前读取的数据不一致,过程不可重复读。

不可重复读字面含义已经很明了了,比如事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。

幻读

⏹主要针对的操作是新增或删除

⏹两次事务的结果

⏹事务A按照特定条件查询出结果,事务B新增了一条符合条件的数据,事务A中查询的数据和数据库中的数据不一致,事务A好像出现了幻觉,这种情况称为幻读。

小的时候数手指,第一次数十10个,第二次数是11个,怎么回事?

产生幻觉了?

幻读也是这样子,事务A首先根据条件索引得到10条数据,然后事务B改变了数据库一条数据,导致也符合事务A当时的搜索条件,这样事务A再次搜索发现有11条数据了,就产生了幻读。

一个对照关系表:

Dirtyreads

non-repeatablereads

phantomreads

Serializable

不会

REPEATABLEREAD

READCOMMITTED

ReadUncommitted

所以最安全的,是Serializable,但是伴随而来也是高昂的性能开销。

另外,事务常用的两个属性:

readonly和timeout

一个是设置事务为只读以提升性能。

另一个是设置事务的超时时间,一般用于防止大事务的发生。

还是那句话,事务要尽可能的小!

最后引入一个问题:

一个逻辑操作需要检查的条件有20条,能否为了减小事务而将检查性的内容放到事务之外呢?

很多系统都是在DAO的内部开始启动事务,然后进行操作,最后提交或者回滚。

这其中涉及到代码设计的问题。

小一些的系统可以采用这种方式来做,但是在一些比较大的系统,逻辑较为复杂的系统中,势必会将过多的业务逻辑嵌入到DAO中,导致DAO的复用性下降。

所以这不是一个好的实践。

来回答这个问题:

能否为了缩小事务,而将一些业务逻辑检查放到事务外面?

答案是:

对于核心的业务检查逻辑,不能放到事务之外,而且必须要作为分布式下的并发控制!

一旦在事务之外做检查,那么势必会造成事务A已经检查过的数据被事务B所修改,导致事务A徒劳无功而且出现并发问题,直接导致业务控制失败。

所以,在分布式的高并发环境下,对于核心业务逻辑的检查,要采用加锁机制。

比如事务开启需要读取一条数据进行验证,然后逻辑操作中需要对这条数据进行修改,最后提交。

这样的一个过程,如果读取并验证的代码放到事务之外,那么读取的数据极有可能已经被其他的事务修改,当前事务一旦提交,又会重新覆盖掉其他事务的数据,导致数据异常。

所以在进入当前事务的时候,必须要将这条数据锁住,使用forupdate就是一个很好的在分布式环境下的控制手段。

一种好的实践方式是使用编程式事务而非生命式,尤其是在较为规模的项目中。

对于事务的配置,在代码量非常大的情况下,将是一种折磨,而且人肉的方式,绝对不能避免这种问题。

将DAO保持针对一张表的最基本操作,然后业务逻辑的处理放入manager和service中进行,同时使用编程式事务更精确的控制事务范围。

特别注意的,对于事务内部一些可能抛出异常的情况,捕获要谨慎,不能随便的catchException导致事务的异常被吃掉而不能正常回滚。

Spring配置声明式事务:

*配置DataSource

*配置事务管理器

*事务的传播特性

*那些类那些方法使用事务

Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。

DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager。

根据代理机制的不同,Spring事务的配置又有几种不同的方式:

第一种方式:

每个Bean都有一个代理

第二种方式:

所有Bean共享一个代理基类

第三种方式:

使用拦截器

第四种方式:

使用tx标签配置的拦截器

第五种方式:

全注解

1、spring事务控制放在service层,在service方法中一个方法调用service中的另一个方法,默认开启几个事务?

spring的事务传播方式默认是PROPAGATION_REQUIRED,判断当前是否已开启一个新事务,有则加入当前事务,否则新开一个事务(如果没有就开启一个新事务),所以答案是开启了一个事务。

2、spring什么情况下进行事务回滚?

Spring、EJB的声明式事务默认情况下都是在抛出uncheckedexception后才会触发事务的回滚

unchecked异常,即运行时异常RunntimeException回滚事务;

checked异常,即Exception可try{}捕获的不会回滚.当然也可配置spring参数让其回滚。

spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行commitorrollback(Spring默认取决于是否抛出runtime异常)。

若抛出runtimeexception并在你的业务方法中没有catch到的话,事务会回滚。

一般不需要在业务方法中catch异常,如果非要catch,在做完你想做的工作后(比如关闭文件等)一定要抛出runtimeexception,否则spring会将你的操作commit,这样就会产生脏数据.所以你的catch代码是画蛇添足。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 农学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1