spring 关于事务的介绍

 http://wanjianfei.iteye.com/category/74637?show_full=true

Spring声明式事务管理及事务嵌套:Spring动态代理的一个重要特征是,它是针对接口的,所以我们的dao要通过动态代理来让spring接管事务,就必须在dao前面抽象出一个接口,当然如果没有这样的接口,那么spring会使用CGLIB来解决问题。 
事务配置 
Spring动态代理的一个重要特征是,它是针对接口的,所以我们的dao要通过动态代理来让spring接管事务,就必须在dao前面抽象出一个接口,当然如果没有这样的接口,那么spring会使用CGLIB来解决问题。 
一般地,使用Spring框架时,可在其applicationContext.xml文件中声明其对hibernate事务的使用: 

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

上述配置是针对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 中有一个方法实现如下 

Java代码 
1./**   
2.* 事务属性配置为 PROPAGATION_REQUIRED   
3.*/    
4.void methodA() {    
5.// 调用 ServiceB 的方法    
6.ServiceB.methodB();    
7.}  
 /** 
 * 事务属性配置为 PROPAGATION_REQUIRED 
 */ 
 void methodA() { 
 // 调用 ServiceB 的方法 
 ServiceB.methodB(); 
 }
那么如果 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 特性呢, 我们用代码来说话 

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

 ServiceB { 
 
 /** 
 * 事务属性配置为 PROPAGATION_REQUIRES_NEW 
 */ 
. void methodB() { 
 }  
 }

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

Java代码 
1.ServiceA {    
2.    
3. /**   
4. * 事务属性配置为 PROPAGATION_REQUIRED   
5. */    
6. void methodA() {    
7. ServiceB.methodB();    
8. }    
9. }    
10. ServiceB {    
11.    
12. /**   
13. * 事务属性配置为 PROPAGATION_NESTED   
14. */    
15. void methodB() {    
16.1 }    
17.}  
ServiceA { 
 
 /** 
 * 事务属性配置为 PROPAGATION_REQUIRED 
 */ 
 void methodA() { 
 ServiceB.methodB(); 
 } 
 } 
 ServiceB { 
 
 /** 
 * 事务属性配置为 PROPAGATION_NESTED 
 */ 
 void methodB() { 
1 } 
}
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 

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

 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { 
 if (!isNestedTransactionAllowed()) { 
 throw new NestedTransactionNotSupportedException( 
 ”Transaction manager does not allow nested transactions by default - ” + 
 ”specify 'nestedTransactionAllowed' property with value 'true'”); 
 } 
 if (debugEnabled) { 
 logger.debug(”Creating nested transaction with name [” + definition.getName() + ”]”); 
 } 
if (useSavepointForNestedTransaction()) { 
 // Create savepoint within existing Spring-managed transaction, 
 // through the SavepointManager API implemented by TransactionStatus. 
 // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization. 
 DefaultTransactionStatus status = 
 newTransactionStatus(definition, transaction, false, false, debugEnabled, null);
 status.createAndHoldSavepoint(); 
 return status; 
 } 
 else { 
 // Nested transaction through nested begin and commit/rollback calls. 
 // Usually only for JTA: Spring synchronization might get activated here 
. // in case of a pre-existing JTA transaction. 
 doBegin(transaction, definition); 
 boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER); 
34. 34. return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null); 
}
}
}
一目了然 
1. 我们要设置 transactionManager 的 nestedTransactionAllowed 属性为 true, 注意, 此属性默认为 false!!! 
再看 AbstractTransactionStatus#createAndHoldSavepoint() 方法 

Java代码 
1./**   
2. * Create a savepoint and hold it for the transaction.   
3. * @throws org.springframework.transaction.NestedTransactionNotSupportedException   
4. * if the underlying transaction does not support savepoints   
5. */    
6. public void createAndHoldSavepoint() throws TransactionException {    
7. setSavepoint(getSavepointManager().createSavepoint());    
8. }  
/** 
 * Create a savepoint and hold it for the transaction. 
 * @throws org.springframework.transaction.NestedTransactionNotSupportedException 
 * if the underlying transaction does not support savepoints 
 */ 
 public void createAndHoldSavepoint() throws TransactionException { 
 setSavepoint(getSavepointManager().createSavepoint()); 
 }
可以看到 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 了. 


 http://tech.ddvip.com/2008-12/122881410698659_2.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是几个关于Spring事务的面试题: 1. Spring事务的传播行为有哪些?它们的含义是什么? 答:Spring事务的传播行为包括REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER和NESTED。它们的含义如下: - REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 - SUPPORTS:支持当前事务,如果当前没有事务,就以非事务的方式执行。 - MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。 - REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。 - NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 - NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 - NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则新建一个事务。 2. Spring事务的隔离级别有哪些?它们的含义是什么? 答:Spring事务的隔离级别包括DEFAULT、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ和SERIALIZABLE。它们的含义如下: - DEFAULT:使用数据库默认的隔离级别。 - READ_UNCOMMITTED:最低的隔离级别,允许读取未提交的数据,存在脏读、不可重复读、幻读等问题。 - READ_COMMITTED:允许读取已提交的数据,避免了脏读,但是仍然存在不可重复读和幻读问题。 - REPEATABLE_READ:保证同一事务中多次读取同一数据时,结果始终一致,避免了脏读和不可重复读,但是仍然存在幻读问题。 - SERIALIZABLE:最高的隔离级别,强制事务串行执行,避免了脏读、不可重复读和幻读,但是会影响并发性能。 3. Spring事务的实现方式有哪些? 答:Spring事务的实现方式包括编程式事务和声明式事务。编程式事务是通过编写代码实现事务控制,比较灵活,适用于复杂的事务场景。声明式事务是通过注解或XML配置实现事务控制,比较简单,适用于简单的事务场景。在实际开发中,通常使用声明式事务来实现事务控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值