首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 开源软件 >

Spring中的事宜传播的几种方式

2013-03-29 
Spring中的事务传播的几种方式Spring中的事务是怎么传播的?Spring 框架中对TransactionDefinition 接口中

Spring中的事务传播的几种方式

Spring中的事务是怎么传播的?

Spring 框架中对TransactionDefinition 接口中的定义 如下:

1.PROPAGATION_REQUIRED
? 支持当前的事务,如果当前没有事务,就新建一个

2.PROPAGATION_SUPPORTS
? 支持当前事务,如果不存在事务执行非事务方式。对于事务同步的事务管理器来说
? Propagation_Supports 跟一点事务没有的方式是有轻微的区别的。当定义一个事务
? 范围时,将会申请同步机制,因此,相同的数据源将共享这个事务范围(取决于事务
? 管理器的实际同步配置)

3.PROPAGATION_MANDATORY
? 支持当前的事务,如果不存在事务,则抛出异常

4.PROPAGATION_REQUIRES_NEW
? 创建一个新的事务,如果当前存在事务,挂起当前的事务
? 新创建的事务方法区域外面所有事务管理器的事务都将被挂起,等待当前方法事务的结束。
? 这一点特别应用于JtaTransactionManager。

5.PROPAGATION_NOT_SUPPORTED
? 执行非事务方式,如果当前存在事务,挂起。
?
6.PROPAGATION_NEVER
? 执行非事务方式,如果当前事务存在,抛出一个异常

7.PROPAGATION_NESTED
? 如果当前事务存在,在一个嵌套的事务中执行,如果当前没有事务,行为方式跟PROPAGATION_REQUIRED相似
? 实际上嵌套事务的创建仅特定事务管理器支持这种方式。(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
? 它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)

几点区别:PROPAGATION_REQUIRES_NEW 与 PROPAGATION_NESTED
PROPAGATION_REQUIRES_NEW:在给定的区域中创建一个新的、独立的内部事务。这个事务有他自己的隔离区域和一系列的锁,
他的事务提交或者是回滚将完全独立于外面的事务。在内部事务开始运行之前,外部事务是会被挂起的,直到内部事务执行结束
,外部事务才会恢复。
例如:这种内部独立的事务机制会应用于通过sequences产生记录的ID,这个时候访问sequence表应当发生在一个独立的事务中,
为了使这个Sequence加锁产生序列的时间尽可能的短;这里应当避免将产生序列的方法跟其他逻辑过程放在一个事务中,这样会
导致Sequence表被锁的时间更长,在一个并发量较高的应用中,如果同一时刻在其他的方法中也需要产生序列,那就要等待,刚才
那个事务的释放,所以这个产生序列的方法应该在一个独立的事务中;

PROPAGATION_NESTED:嵌套事务是当前事务一个真实的子事务。当嵌套事务开始时,保存点(savepoint)将形成。如果嵌套事务失败,
将回滚到嵌套事务开始的保存点。嵌套事务又是外面事务的一部分,所以嵌套事务仅当外事务结束时候才会被提交。

这两个事务最大的区别在于PROPAGATION_REQUIRES_NEW是一个全新的事务,完全独立;而PROPAGATION_NESTED是外部事务的子事务
外部事务commit,子事务也会被commit;如果外部事务rollback,子事务也会被rollback;

举个spring事务使用的例子:
由于记账扣款比较频繁,考虑将生成的记账分录批量进行扣款处理;每笔交易有两笔分录(CR与DR);事务配置:PROPAGATION_REQUIRES

扣款的方法如下:
@Transactional
public void updateBalanceByCollection(MemberAccount memberAccount,List<AccountingEntry> entryList) {//同一个账户下面的所有未记账成功的分录
??? BigDecimal balance = memberAccount.getBalance();
??? for (AccountingEntry accountingEntry : entryList) {
??? ??? AccountingDeal accountingDeal =??? accountingDealService.findById(accountingEntry.getDealId());
??? ??? if(accountingDeal.getStatus().intValue()==0){
??? ??? ??? ??? switch (accountingEntry.getChangeType().intValue()) {
??? ??? ??? ??? case 1:
??? ??? ??? ??? ??? balance = balance.add(accountingEntry.getChangeAmount());
??? ??? ??? ??? ??? break;
??? ??? ??? ??? case 2:
??? ??? ??? ??? ??? balance = balance.subtract(accountingEntry.getChangeAmount());
??? ??? ??? ??? ??? break;
??? ??? ??? ??? }
??? ??? ??? ??? accountingEntry.setStatus(1);//标记为变更成功
??? ??? ??? ??? accountingEntryService.update(accountingEntry);

??????????????? //由于事务此处不是立即保存到数据库的,需等到方法执行完毕
??? ??? ??? ??? if(accountingEntryService.isUnSuccess(accountingEntry.getDealId(),accountingEntry.getId())){

??????????????????? //当另外一条记账分录也扣款成功时,将此笔交易置为成功
??? ??? ??? ??? ??? //deal对应的分录是都已经成功过账则更新deal状态
??? ??? ??? ??? ??? accountingDealService.updateSuccess(accountingEntry.getDealId());
??? ??? ??? ??? }
??? ??? }
??? }
??? memberAccountService.updateMemberBalance(balance, memberAccount.getMerchantId(), memberAccount.getAccountCode());//批量更新会员账户余额
}
事务在spring中配置了PROPAGATION_REQUIRES类型? 如果在此方法中调用的类中没有事务,沿用当前的事务,如accountingDealService.updateSuccess,accountingEntryService.update均沿用了
当前方法的事务

问题:此方法如果出现异常,将会回滚掉for循环中所有的记录

解决方法:可以将数据库更新的部分放在单独的一个方法中,此方法是用PROPAGATION_NESTED 如果当前分录保存时发生异常,只回滚一条数据,不至于全部回滚

热点排行