`
newchq
  • 浏览: 54986 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论
阅读更多

事务配置

Spring动态代理的一个重要特征是,它是针对接口的,所以我们的dao要通过动态代理来让spring接管事务,就必须在dao前面抽象出一个接口,当然如果没有这样的接口,那么spring会使用CGLIB来解决问题。

一般地,使用Spring框架时,可在其applicationContext.xml文件中声明其对hibernate事务的使用:

1. <bean id=”tranManager”
2. class=”org.springframework.orm.hibernate3.HibernateTransactionManager”>
3. <property name=”sessionFactory”>
4. <ref bean=”SessionFactoryID” />
5. </property>
6. </bean>
7. <bean id=”transactionInterceptor”
8. class=”org.springframework.transaction.interceptor.TransactionInterceptor”>
9. <property name=”transactionManager”>
10. <ref bean=”tranManager” />
11. </property>
12. <property name=”transactionAttributes”>
13. <props>
14. <prop key=”*”>PROPAGATION_REQUIRED</prop>
15. </props>
16. </property>
17. </bean>
18. <bean id=”proxyCreator”
19. class=”org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator”>
20. <property name=”interceptorNames”>
21. <list>
22. <value>transactionInterceptor</value>
23. </list>
24. </property>
25. <property name=”beanNames”>
26. <list>
27. <value>*Biz</value>
28. </list>
29. </property>
30. </bean>
31.

上述配置是针对Biz后缀的所有接口类中声明的方法进行了事务配置,其事务的传播策略为PROPAGATION_REQUIRED,在在 spring 中一共定义了六种事务传播属性:

PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。


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

事务嵌套

在我所见过的误解中, 最常见的是下面这种:
引用

假如有两个业务接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一个方法实现如下

1. /**
2. * 事务属性配置为 PROPAGATION_REQUIRED
3. */
4. void methodA() {
5. // 调用 ServiceB 的方法
6. ServiceB.methodB();
7. }

那么如果 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 启动一个新的, 不依赖于环境的 ”内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.


另一方面, PROPAGATION_NESTED 开始一个 ”嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.

由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.


那么外部事务如何利用嵌套事务的 savepoint 特性呢, 我们用代码来说话

1. 1. ServiceA {
2. 2.
3. 3. /**
4. 4. * 事务属性配置为 PROPAGATION_REQUIRED
5. 5. */
6. 6. void methodA() {
7. 7. ServiceB.methodB();
8. 8. }
9. 9.
10. 10. }
11. 11.
12. 12. ServiceB {
13. 13.
14. 14. /**
15. 15. * 事务属性配置为 PROPAGATION_REQUIRES_NEW
16. 16. */
17. 17. void methodB() {
18. 18. }
19. 19.
20. 20. }


这种情况下, 因为 ServiceB#methodB 的事务属性为 PROPAGATION_REQUIRES_NEW, 所以两者不会发生任何关系, ServiceA#methodA 和 ServiceB#methodB 不会因为对方的执行情况而影响事务的结果, 因为它们根本就是两个事务, 在 ServiceB#methodB 执行时 ServiceA#methodA 的事务已经挂起了 (关于事务挂起的内容已经超出了本文的讨论范围, 有时间我会再写一些挂起的文章) .

那么 PROPAGATION_NESTED 又是怎么回事呢? 继续看代码

1. 1. ServiceA {
2. 2.
3. 3. /**
4. 4. * 事务属性配置为 PROPAGATION_REQUIRED
5. 5. */
6. 6. void methodA() {
7. 7. ServiceB.methodB();
8. 8. }
9. 9.
10. 10. }
11. 11.
12. 12. ServiceB {
13. 13.
14. 14. /**
15. 15. * 事务属性配置为 PROPAGATION_NESTED
16. 16. */
17. 17. void methodB() {
18. 18. }
19. 19.
20. 20. }

1. ServiceA { 2. 3. /** 4. * 事务属性配置为 PROPAGATION_REQUIRED 5. */ 6. void methodA() { 7. ServiceB.methodB(); 8. } 9. 10. } 11. 12. ServiceB { 13. 14. /** 15. * 事务属性配置为 PROPAGATION_NESTED 16. */ 17. void methodB() { 18. } 19. 20. }

这种方式也是嵌套事务最有价值的地方, 它起到了分支执行的效果, 如果 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

1. 1. /**
2. 2. * Create a TransactionStatus for an existing transaction.
3. 3. */
4. 4. private TransactionStatus handleExistingTransaction(
5. 5. TransactionDefinition definition, Object transaction, boolean debugEnabled)
6. 6. throws TransactionException {
7. 7.
8. 8. ... 省略
9. 9.
10. 10. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
11. 11. if (!isNestedTransactionAllowed()) {
12. 12. throw new NestedTransactionNotSupportedException(
13. 13. ”Transaction manager does not allow nested transactions by default - ” +
14. 14. ”specify 'nestedTransactionAllowed' property with value 'true'”);
15. 15. }
16. 16. if (debugEnabled) {
17. 17. logger.debug(”Creating nested transaction with name [” + definition.getName() + ”]”);
18. 18. }
19. 19. if (useSavepointForNestedTransaction()) {
20. 20. // Create savepoint within existing Spring-managed transaction,
21. 21. // through the SavepointManager API implemented by TransactionStatus.
22. 22. // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
23. 23. DefaultTransactionStatus status =
24. 24. newTransactionStatus(definition, transaction, false, false, debugEnabled, null);
25. 25. status.createAndHoldSavepoint();
26. 26. return status;
27. 27. }
28. 28. else {
29. 29. // Nested transaction through nested begin and commit/rollback calls.
30. 30. // Usually only for JTA: Spring synchronization might get activated here
31. 31. // in case of a pre-existing JTA transaction.
32. 32. doBegin(transaction, definition);
33. 33. boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
34. 34. return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
35. 35. }
36. 36. }
37. 37. }

一目了然

1. 我们要设置 transactionManager 的 nestedTransactionAllowed 属性为 true, 注意, 此属性默认为 false!!!

再看 AbstractTransactionStatus#createAndHoldSavepoint() 方法

1. 1. /**
2. 2. * Create a savepoint and hold it for the transaction.
3. 3. * @throws org.springframework.transaction.NestedTransactionNotSupportedException
4. 4. * if the underlying transaction does not support savepoints
5. 5. */
6. 6. public void createAndHoldSavepoint() throws TransactionException {
7. 7. setSavepoint(getSavepointManager().createSavepoint());
8. 8. }

可以看到 Savepoint 是 SavepointManager.createSavepoint 实现的, 再看 SavepointManager 的层次结构, 发现
其 Template 实现是 JdbcTransactionObjectSupport, 常用的 DatasourceTransactionManager, HibernateTransactionManager 中的 TransactonObject 都是它的子类 :

JdbcTransactionObjectSupport 告诉我们必须要满足两个条件才能 createSavepoint :

2. java.sql.Savepoint 必须存在, 即 jdk 版本要 1.4+
3. Connection.getMetaData().supportsSavepoints() 必须为 true, 即 jdbc drive 必须支持 JDBC 3.0

确保以上条件都满足后, 你就可以尝试使用 PROPAGATION_NESTED 了.

分享到:
评论

相关推荐

    spring事务配置的五种方式

    ### Spring事务配置的五种方式详解 #### 一、引言 在企业级应用开发中,事务处理是非常重要的一部分,特别是在涉及多个数据库操作时。Spring框架提供了强大的事务管理功能,支持编程式和声明式两种事务处理方式。...

    spring事务配置的5中方式

    Spring 事务配置是Spring框架中不可或缺的一部分,它用于管理和协调应用程序中的事务边界,确保数据的一致性和完整性。在Spring中,事务配置主要涉及到三个核心组件:DataSource、TransactionManager和代理机制。...

    SSH事务配置大全

    ### SSH事务配置详解 在软件开发领域,特别是Java企业级应用开发中,Spring与Hibernate作为两个重要的框架,常被一起使用来实现业务逻辑层的高效处理。其中,Spring提供了强大的依赖注入(DI)和面向切面编程(AOP...

    JAVA事务配置总结

    本文将对三种不同的事务配置方式进行深入探讨:本地事务、全局事务以及结合动态数据源的事务管理。这些配置方式在处理多数据源和复杂业务场景时具有不同的优势和适用性。 首先,我们来看“本地事务”。本地事务通常...

    详细说明spring事务配置的5种方式

    本文将详细介绍Spring事务配置的五种方式,帮助你深入理解如何在Spring应用中管理事务。 1. **基于XML的声明式事务管理** 第一种方式是在每个Bean上使用代理来实现事务管理。首先,配置`DataSource`,通常是`...

    spring几种事务配置详解【精】

    本文将深入探讨Spring中的几种事务配置方式,帮助开发者更好地理解和运用。 1. **编程式事务管理** 编程式事务管理是在代码中显式调用事务API来控制事务的开始、提交、回滚等操作。这种方式直接在业务逻辑代码中...

    spring声明式事务配置

    根据提供的信息,我们可以深入探讨Spring框架中的声明式事务配置及其多种实现方式。声明式事务管理是一种简化事务管理的方式,它允许开发人员通过配置而非编程来指定事务边界,从而减少了代码的复杂性并提高了可维护...

    Spring声明式事务配置管理方法

    Spring声明式事务配置管理方法

    mysql支持事务配置,配置InnoDB引擎

    ### MySQL支持事务配置及启用InnoDB引擎详解 #### 一、引言 MySQL是一种广泛使用的开源关系型数据库管理系统,以其高性能、稳定性和易用性而闻名。其中,InnoDB存储引擎因其支持事务处理、行级锁定和外键等功能,...

    spring与mybatis整合实现事务配置

    4. **事务配置**:在Spring中,事务管理有两种方式:编程式事务管理和声明式事务管理。通常我们采用声明式事务管理,通过`&lt;tx:annotation-driven&gt;`标签启用基于注解的事务管理。事务的传播行为、隔离级别、超时时间...

    Spring事务配置的五种方式

    Spring 事务配置是Spring框架中不可或缺的部分,它用于管理和协调应用程序中的事务边界,确保数据的一致性和完整性。在Spring中,事务配置主要涉及到三个核心组件:DataSource、TransactionManager以及代理机制。...

    spring,struts,hibernate,事务配置

    本文将详细介绍这三大框架在事务配置方面的知识,以及如何进行通用的事务管理设置。 1. **Spring 事务管理** Spring 提供了两种事务管理方式:编程式事务管理和声明式事务管理。编程式事务管理通过 `...

    Spring使用TransactionProxyFactoryBean声明式事务配置实例

    在Spring框架中,声明式事务管理是通过元数据(如XML配置或注解)来定义事务边界,使得开发者无需在业务代码中显式调用事务管理API,即可实现事务的控制。TransactionProxyFactoryBean是Spring提供的一种工具,用于...

    spring事务配置详解

    Spring提供了多种事务配置方式,主要涉及三个方面:DataSource、TransactionManager和代理机制。下面将详细解释这五个配置方法。 1. **DataSource**:DataSource是Spring与数据库交互的基础,通常负责连接池的管理...

Global site tag (gtag.js) - Google Analytics