天天看点

Spring 编程式事务管理 TransactionTemplate 不过不常用,貌似灵活

1, 一直以来, 在用Spring进行事物管理时, 只知道用声明式的策略, 即根据不同的数据源, 配置一个事物管理器(TransactionManager), 通过配置切面(PointCut)应用到相应的业务方法上或者直接在方法上加@Ttransactional注解.这种事务管理使用起来比较简单、

2,TransactionTemplate 编程式事务管理,需要自己手动在每个业务方法中实现事务。

如下:

在DAO层的配置文件中, 配置TransactionTemplate, 需要注入TransactionManager

<bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="transactionTemplate"
          class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
        </property>
    </bean>
           

将TransactionTemplate注入到业务层方法中, 并使用:首先分析一下TransactionTemplate的核心原理:

TransactionTemplate核心方法:

public class TransactionTemplate extends DefaultTransactionDefinition
        implements TransactionOperations, InitializingBean {

    @Nullable
    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
        Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
        if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
            return ((CallbackPreferringPlatformTransactionManager)this.transactionManager).execute(this, action);
         } else {
            TransactionStatus status = this.transactionManager.getTransaction(this);

            Object result;
            try {
                result = action.doInTransaction(status);
            } catch (Error | RuntimeException var5) {
                this.rollbackOnException(status, var5);
                throw var5;
            } catch (Throwable var6) {
                this.rollbackOnException(status, var6);
                throw new UndeclaredThrowableException(var6, "TransactionCallback threw undeclared checked exception");
            }

            this.transactionManager.commit(status);
            return result;
        }
    }
}
           

由上面的代码可以推测到, 真正执行业务方法的关键代码是: action.doInTransaction(status);

正好, 有个入参TransactionCallback<T>, 翻看该接口的源码:

@FunctionalInterface
public interface TransactionCallback<T> {
    @Nullable
    T doInTransaction(TransactionStatus var1);
}
           

该接口只有一个doInTransaction方法, 那么很简单, 我们可以通过匿名内部类的方式将业务代码放在doInTransaction中:

举例如下:

@Autowired
 private TransactionTemplate transactionTemplate;
  
 @Autowired
 private PayOrderDAO payOrderDAO; 
  
/** 
 * 保存支付订单 
 */  
protected PayOrder savePayReq(final PayOrder payOrder) {  
    PayOrder order = (PayOrder) this.transactionTemplate  
            .execute(new TransactionCallback() {  
                @Override  
                public Object doInTransaction(TransactionStatus status) {  
                    // 查看是否已经存在支付订单,如果已经存在则返回订单主键  
                    PayOrder payOrderTemp = payOrderDAO.findOrder(String  
                            .valueOf(payOrder.getPayOrderId()));  
  
                    // 由支付渠道类型(PayChannelType)转换得到交易类型(PayType)  
                    if (payOrder.getPayChannelId().equalsIgnoreCase(PAY_CHNL_ACT_BAL)) {// 账户余额支付  
                        payOrder.setPayType("3");  
                    } else if (payOrder.getPayChannelId().equalsIgnoreCase(PAY_CHNL_FAST_PAY)) {// 联通快捷支付  
                        payOrder.setPayType("4");  
                    } else {// 网银网关支付  
                        payOrder.setPayType("2");  
                    }  
  
                    // 比对新的支付金额与原订单金额是否一致,如不一致则提示错误  
                    if (payOrderTemp == null) {  
                        String orderId = payOrderDAO.save(payOrder);  
                        payOrder.setPayOrderId(orderId);  
                        return payOrder;  
                    } else {  
                        return payOrderTemp;  
                    }  
                }  
            });  
    if ("2".equals(order.getOrderState())) {// 2:表示支付成功  
        throw new EpaymentBizException(StatusCode.DQSystem.PAY_FAIL,  
                "同一订单不能重复支付");  
    } else if (payOrder.getPayAmt().longValue() != order.getPayAmt()  
            .longValue()) {  
        throw new EpaymentBizException(StatusCode.DQSystem.PAY_FAIL,  
                "交易金额与原订单不一致");  
    } else {  
        return payOrder;  
    }  
  
}