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循环中所有的记录

猜你喜欢

转载自tedtian.iteye.com/blog/1835564
今日推荐