Spring事务管理的两种模式

来源:互联网 发布:阿富汗 帝国坟场 知乎 编辑:程序博客网 时间:2024/06/15 07:23

Spring事务管理的两种模式

1.即通过数据库实现提供的事务机制,通过编码方式进行事务管理。

   如通过JDBC、JTA 的rollback、commit方法;HibernateTransaction 的rollback、commit方法等。这种方法大家已经相当熟悉。

 

以下是通过c3p0提供的事务机制实现的事务管理方法

private DataSourceTransactionManagertransactionManager;

         privateDefaultTransactionDefinition transactionDefinition;

         privateTransactionStatus transactionStatus;

 

 

         // 设置SavePoint

         publicvoid setTransactionStatus() {

                   transactionStatus= transactionManager.getTransaction(transactionDefinition);

         }

 

    // 提交事务

         publicboolean transactionCommit() {

                   try{

                            transactionManager.commit(transactionStatus);

                   }catch (DataAccessException e) {

                            //transactionManager.rollback(transactionStatus);

                            returnfalse;

                   }

                   returntrue;

         }

   // 回滚事务

         publicvoid transactionRollback() {

                   transactionManager.rollback(transactionStatus);

         }

 

2. 依赖SPRING容器的声明式事务管理

以下重点讲讲声明式事务管理

 

 

        

 

 

 

 

 

 

 

声明式事务管理

spring特有的事务传播行为,确定客户端和被调用端的事务边界(对service的进行调用时对service的事务边界进行控制),共有7种控制行为。

传播行为

含义

PROPAGATION_REQUIRED(XML文件中为REQUIRED)

表示当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)                                             

PROPAGATION_SUPPORTS(XML文件中为SUPPORTS)

表示当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行

PROPAGATION_MANDATORY(XML文件中为MANDATORY)

表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常

PROPAGATION_NESTED(XML文件中为NESTED)

表示如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同PROPAGATION_REQUIRED的一样

PROPAGATION_NEVER(XML文件中为NEVER)

表示当方法不应该在一个事务中运行,如果存在一个事务,则抛出异常

PROPAGATION_REQUIRES_NEW(XML文件中为REQUIRES_NEW)

表示当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行

PROPAGATION_NOT_SUPPORTED(XML文件中为NOT_SUPPORTED)         

表示该方法不应该在一个事务中运行。如果有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行

 

 

 

以上是SPRING官方给出的对控制行为的描述,但事务的处理“依赖”什么来“触发”,在实际应用中有什么需要注意的?

 

默认情况下,只在方法抛出运行时异常的时候才回滚(runtime exception)。而在受检查异常(checkedexception)时不回滚事务。

  

   当然Spring也提供了指定异常回滚事务的选择,因此也可在抛出受检查异常时,回滚事务,在配置文件中通过"rollback-for"关键字可以让你指定回滚事务的异常

   比如:

 

 

<tx:advice id="txAdvice"transaction-manager="txManager"> 

  <tx:attributes> 

   <tx:method name="processChgList"propagation="REQUIRED" <span style="color:#ff0000;"><span style="color:#888888;">rollback-for="Exception"</span></span>/> 

    </tx:attributes> 

</tx:advice> 

   当然,如果你捕捉并自行处理了异常(意味着不再向外抛出),那么Spring的事务回滚再此处不再生效

 

 

再大部分时候,我们只需要用到REQUIRED, REQUIRES_NEW,NESTED三种事务机制,在此重点讲讲REQUIRED, REQUIRES_NEW,NESTED(其他没研究过,不好乱讲)

 

REQUIED

 

在Spring项目下,有FlightChgProcessService

 

FlightChgProcessService主要进行变更处理,提供如下服务:锁数据表,对航班数据进行变更处理,等等。

因此,FlightChgProcessService实现了如下两个方法

 

List<Map<String, SqlBuildEntity>>lockNGeneratePairingSql(FlightChgEntity flightChgInfo);

void processChgList(List<FlightChgEntity>flightChgList);

 

因为数据库是一个共享数据库,可能有我不了解的应用在更新数据库

我希望

1、  锁处理成功后,才对被锁住的航班数据进行变更处理,避免数据库死锁,脏数据,或者幻读

2、  锁处理失败后,不进行航班数据变更,同时回滚到锁处理之前的savePoint

3、  锁处理成功,但是航班数据更新失败,应当回归到锁处理之前的savePoint,也就是说

lockNGeneratePairingSql、processChgList在一个事务中

于是我将FlightChgProcessService提供的服务都定义为同一种传播机制REQUIED

 

如下配置:

 

<bean id="baseRequiedProxy"

                   class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"

                   lazy-init="true">

                   <propertyname="transactionManager">

                            <refbean="transactionManager" />

                   </property>

                   <propertyname="transactionAttributes">

                            <props>

                                     <propkey="*">PROPAGATION_REQUIRED</prop>

                            </props>

                   </property>

         </bean>

 

 

<bean id="flightChgProcessService"parent="baseRequiedProxy">

                   <propertyname="target">

                            <beanclass="com.csair.soc.fltManager.impl.FlightChgProcessServiceImpl">

                                     <propertyname="flightChgRelManagerService">

                                               <refbean="flightChgRelManagerService" />

                                     </property>

                                     <propertyname="flightChgRelLoader">

                                               <refbean="flightChgRelLoader" />

                                     </property>

                            </bean>

                   </property>

         </bean>

当然,我们也可以针对某个具体的方法进行配置,也是可以的,具体可以参看

SpringInAction

 

processChgList(List<FlightChgEntity>flightChgList);

{

For(FlightChgEntity flightChgInfo : flightChgList)

{

lockNGeneratePairingSql(flightChgInfo);

}

}

 

NESTED

除了锁表,更新航班数据外,我们还需要根据锁表,更新航班数据的结果去设置这次变更的状态(比如成功、失败、锁表下次再更新),方法名为setFinalProcessResult(),属于服务FlightChgRelManagerService

我们希望:

 

1、  锁表并更新航班数据可以单独提交或者回滚,根据执行的结果设置变更的状态

2、  如果变更状态失败,那么锁表并更新航班数的操作要回滚

 

 

这个时候我们可以这样设置

<bean id="flightChgProcessService"parent="baseNestedProxy">

<bean id="FlightChgRelManagerService"parent="baseRequiedProxy">

 

 

setFinalProcessResult()

{

String Status=”Successed”

Try{

processChgList(List<FlightChgEntity>flightChgList)

}catch(DataAccessException){

Status=”Failed”;

}

updateFlightChgTableStatus(Status)

 

}

…………………..

 

 

 

对于NESTED内层事务而言

1、  内层事务独立于外层事务,可以独立递交或者回滚

2、  内层事务回滚到开始执行的savePoint后,可以继续向下执行,不对外层事务造成影响

3、  外层事务回滚,那么内层事务也同时回滚。

说得直白点,其实内层事务就是外层事务的一个分支

 

 

 

REQUIED_NEW

 

 

 

Spring的DAO框架没有抛出与特定技术相关的异常,例如SQLException或HibernateException,抛出的异常都是与特定技术无关的org.springframework.dao.DataAccessException类的子类,避免系统与某种特殊的持久层实现耦合在一起。DataAccessException是RuntimeException,是一个无须检测的异常,不要求代码去处理这类异常,遵循了Spring的一般理念:异常检测会使代码到处是不相关的catch或throws语句,使代码杂乱无章;并且NestedRuntimeException的子类,是可以通过NestedRuntimeException的getCause()方法获得导致该异常的另一个异常

 


0 0