Spring事务传播机制

 

 

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法事务方法发生嵌套调用时事务如何进行传播,
即协调已经有事务标识的方法之间的发生调用时的事务上下文的规则(是否要有独立的事务隔离级别和锁)
html

 

概述spring

当咱们调用一个基于Spring的Service接口方法(如UserService#addUser())时,它将运行于Spring管理的事务 环境中,Service接口方法可能会在内部调用其它的Service接口方法以共同完成一个完整的业务操做,所以就会产生服务接口方法嵌套调用的状况, Spring经过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。apache

事务传播是Spring进行事务管理的重要概念,其重要性怎么强调都不为过。可是事务传播行为也是被误解最多的地方,在本文里,咱们将详细分析不一样事务传播行为的表现形式,掌握它们之间的区别。ui

事务传播行为种类spa

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法事务方法发生嵌套调用时事务如何进行传播:日志

1事务传播行为类型htm

事务传播行为类型blog

说明接口

PROPAGATION_REQUIRED事务

若是当前没有事务,就新建一个事务,若是已经存在一个事务中,加入到这个事务中。这是最多见的选择。

PROPAGATION_SUPPORTS

支持当前事务,若是当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY

使用当前的事务,若是当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW

新建事务,若是当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED

以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER

以非事务方式执行,若是当前存在事务,则抛出异常。

PROPAGATION_NESTED

若是当前存在事务,则在嵌套事务内执行。若是当前没有事务,则执行与PROPAGATION_REQUIRED相似的操做。

当使用PROPAGATION_NESTED时,底层的数据源必须基于JDBC 3.0,而且实现者须要支持保存点事务机制。

几种容易引发误解的组合事务传播行为

当服务接口方法分别使用表1中不一样的事务传播行为,且这些接口方法又发生相互调用的状况下,大部分组合都是一目了然,容易理解的。可是,也存在一些容易引发误解的组合事务传播方式。

下面,咱们经过两个具体的服务接口的组合调用行为来破解这一难点。这两个服务接口分别是UserService和ForumService, UserSerice有一个addCredits()方法,ForumSerivce#addTopic()方法调用了 UserSerice#addCredits()方法,发生关联性服务方法的调用:

public class ForumService {

private UserService userService;

public void addTopic(){①调用其它服务接口的方法

//add Topic…

userService.addCredits();②被关联调用的业务方法

}

}

嵌套调用的事务方法

对Spring事务传播行为最多见的一个误解是:当服务接口方法发生嵌套调用时,被调用的服务方法只能声明为 PROPAGATION_NESTED。这种观点犯了望文生义的错误,误认为PROPAGATION_NESTED是专为方法嵌套准备的。这种误解遗害不 浅,执有这种误解的开发者错误地认为:应尽可能不让Service类的业务方法发生相互的调用,Service类只能调用DAO层的DAO类,以免产生嵌 套事务。

其实,这种顾虑是彻底没有必要的,PROPAGATION_REQUIRED已经清楚地告诉咱们:事务的方法会足够“聪明”地判断上下文是否已经存在一个事务中,若是已经存在,就加入到这个事务中,不然建立一个新的事务。

依照上面的例子,假设咱们将ForumService#addTopic()和UserSerice#addCredits()方法的事务传播行为都设置为PROPAGATION_REQUIRED,这两个方法将运行于同一个事务中。

为了清楚地说明这点,能够将Log4J的日志设置为DEBUG级别,以观察Spring事务管理器内部的运行状况。下面将两个业务方法都设置为PROPAGATION_REQUIRED,Spring所输出的日志信息以下:

Using transaction object

[org.springframework.jdbc.datasource.DataSourceTransactionManager$DataSourceTransactionObject@e3849c]

①为ForumService#addTopic()新建一个事务

Creating new transaction with name [com.baobaotao.service.ForumService.addTopic]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

Acquired Connection [org.apache.commons.dbcp.PoolableConnection@dc41c5] for JDBC transaction

Switching JDBC Connection [org.apache.commons.dbcp.PoolableConnection@dc41c5] to manual commit

Bound value [org.springframework.jdbc.datasource.ConnectionHolder@ee1ede] for key [org.apache.commons.dbcp.BasicDataSource@4204] to thread [main]

Initializing transaction synchronization

Getting transaction for [com.baobaotao.service.ForumService.addTopic]

Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@ee1ede] for key [org.apache.commons.dbcp.BasicDataSource@4204] bound to thread [main]

Using transaction object [org.springframework.jdbc.datasource.DataSourceTransactionManager$DataSourceTransactionObject@8b8a47]

UserService#addCredits()简单地加入到已存在的事务中(即①处建立的事务)

Participating in existing transaction

Getting transaction for [com.baobaotao.service.UserService.addCredits]

Completing transaction for [com.baobaotao.service.UserService.addCredits]

Completing transaction for [com.baobaotao.service.ForumService.addTopic]

Triggering beforeCommit synchronization

Triggering beforeCompletion synchronization

Initiating transaction commit

③调用底层Connection#commit()方法提交事务

Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@dc41c5]

Triggering afterCommit synchronization

Triggering afterCompletion synchronization

Clearing transaction synchronization

嵌套事务

将ForumService#addTopic()设置为PROPAGATION_REQUIRED时, UserSerice#addCredits()设置为PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、 PROPAGATION_MANDATORY时,运行的效果都是一致的(固然,若是单独调用addCredits()就另当别论了)。

当addTopic()运行在一个事务下(如设置为PROPAGATION_REQUIRED),而addCredits()设置为 PROPAGATION_NESTED时,若是底层数据源支持保存点,Spring将为内部的addCredits()方法产生的一个内嵌的事务。若是 addCredits()对应的内嵌事务执行失败,事务将回滚到addCredits()方法执行前的点,并不会将整个事务回滚。内嵌事务是内层事务的一 部分,因此只有外层事务提交时,嵌套事务才能一并提交。

嵌套事务不可以提交,它必须经过外层事务来完成提交的动做,外层事务的回滚也会形成内部事务的回滚。


嵌套事务和新事务

PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED也是容易混淆的两个传播行为。
PROPAGATION_REQUIRES_NEW 启动一个新的、和外层事务无关的“内部”事务。该事务拥有本身的独立隔离级别,不依赖于外部事务,独立地提交和回滚。当内部事务开始执行时,外部事务 将被挂起,内务事务结束时,外部事务才继续执行。

因而可知, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于:
PROPAGATION_REQUIRES_NEW 将建立一个全新的事务,它和外层事务没有任何关系,

PROPAGATION_NESTED 将建立一个依赖于外层事务的子事务,当外层事务提交或回滚时,子事务也会连带提交和回滚。

其它须要注意问题

如下几个问题值得注意:

1) 当业务方法被设置为PROPAGATION_MANDATORY时,它就不能被非事务的业务方法调用。
如将ForumService#addTopic ()设置为PROPAGATION_MANDATORY,若是展示层的Action直接调用addTopic()方法,将引起一个异常。
正确的状况是: addTopic()方法必须被另外一个带事务的业务方法调用(如ForumService#otherMethod())。
因此 PROPAGATION_MANDATORY的方法通常都是被其它业务方法间接调用的。

2) 当业务方法被设置为PROPAGATION_NEVER时,它将不能被拥有事务的其它业务方法调用。假设UserService#addCredits ()设置为PROPAGATION_NEVER,当ForumService# addTopic()拥有一个事务时,addCredits()方法将抛出异常。因此PROPAGATION_NEVER方法通常是被直接调用的。

3)当方法被设置为PROPAGATION_NOT_SUPPORTED时,外层业务方法的事务会被挂起,当内部方法运行完成后,外层方法的事务从新运行。若是外层方法没有事务,直接运行,不须要作任何其它的事。

小结

在Spring声明式事务管理的配置中,事务传播行为是最容易被误解的配置项,缘由在于事务传播行为名称(如 PROPAGATION_NESTED:嵌套式事务)和代码结构的相似性上(业务类方法嵌套调用另外一个业务类方法)。这种误解在不少Spring开发者中 普遍存在,本文深刻讲解了Spring事务传播行为对业务方法嵌套调用的真实影响,但愿能帮助读者化解对事务传播行为的困惑。

http://www.cnblogs.com/aurawing/articles/1887030.html

相关文章
相关标签/搜索