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()方法获得导致该异常的另一个异常