事务配置
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几种事务配置详解【精】
Spring声明式事务配置管理方法
Spring使用TransactionProxyFactoryBean声明式事务配置实例
详细说明spring事务配置的5种方式 让你对spring 有更深入的认识
mysql支持事务配置,启动InnoDB引擎的方法.
全面解析spring事务配置的五种方式,好不容易找到的
前几天搞 Spring + Hibernate + Struts 事务配置 ,网上找了好多资料,不过好无语,大多都是 Ctrl + V,浪费俺的宝贵时间 现在我总结配出一套,给大家参考参考,可能有不足,请大家多多交流。 附:内有弹出...
struts2、spring与mybatis整合 包含简单登录功能 事务配置,运行JunitTestVillageArticle的modify测试。配置说明参考注释
1.本地事务+动态数据源+单sessionFactory 2.全局事务+动态数据源+单sessionFactory 3.全局事务+多sessionFactory
一、 XML,使用tx标签配置拦截器实现事务 一、 Annotation方式 以下所使用环境为Spring4.0.3、Hibernate4.3.5
很全面的介绍spring的事务配制方法。初学者可以看看
Spring的事务配置的五种方式 Spring的事务配置的五种方式 Spring的事务配置的五种方式 Spring的事务配置的五种方式
分步式事务配置,多个数据库 分步式事务配置,多个数据库
各种事物配置方法。用于工作学习都可以获得很好的知识积累
Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 DataSource、TransactionManager这两部分只是会...
J2EE SSH事务配置 J2EE SSH事务配置 J2EE SSH事务配置 J2EE SSH事务配置
<?xml version="1.0" encoding="UTF-8"?><beansxmlns=...
Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 DataSource、TransactionManager这两部分只是会...