
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:
| 事务传播行为类型 | 说明 |
| PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
| PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
| PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
| PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
| PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
| PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
| PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
当使用PROPAGATION_NESTED时,底层的数据源必须基于JDBC 3.0,并且实现者需要支持保存点事务机制。
几种容易引起误解的组合事务传播行为
当服务接口方法分别使用表1中不同的事务传播行为,且这些接口方法又发生相互调用的情况下,大部分组合都是一目了然,容易理解的。但是,也存在一些容易引起误解的组合事务传播方式。
下面的例子中有两个service:UserService和ForumService, UserSerice有一个addCredits()方法,ForumSerivce#addTopic()方法调用了 UserSerice#addCredits()方法,发生关联性服务方法的调用:
public class ForumService {
private UserService userService;
public void addTopic(){ //①调用其它服务接口的方法
userService.addCredits();//②被关联调用的业务方法
}
}
开发者认为:应尽量不让Service类的业务方法发生相互的调用,Service类只能调用DAO层的DAO类,以避免产生嵌 套事务。其实,这种顾虑是完全没有必要的,PROPAGATION_REQUIRED已经清楚地告诉我们:事务的方法会足够“聪明”地判断上下文是否已经存在一个事务中,如果已经存在,就加入到这个事务中,否则创建一个新的事务。
依照上面的例子,假设我们将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_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事务传播行为对业务方法嵌套调用的真实影响,希望能帮助读者化解对事务传播行为的困惑。
