1, 一直以來, 在用Spring進行事物管理時, 只知道用聲明式的策略, 即根據不同的數據源, 配置一個事物管理器(TransactionManager), 通過配置切面(PointCut)應用到相應的業務方法上或者直接在方法上加@Ttransactional注解.
這種事務管理使用起來比較簡單,但個人感覺靈活性欠缺了點.
2, 最近看公司項目代碼, 發現有位同事在他的模塊了用了另外一種事務管理方式, 查了一下,TransactionTemplate是編程式事務管理.需要自己手動在每個業務方法中實現事務.
3, TransactionTemplate使用(不一定全面):
A, 在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>
B, 將TransactionTemplate注入到業務層方法中, 並使用:
首先分析一下TransactionTemplate的核心原理:
TransactionTemplate核心方法:
1 public class TransactionTemplate extends DefaultTransactionDefinition
2 implements TransactionOperations, InitializingBean {
3
4
5 public <T> T execute(TransactionCallback<T> action) throws TransactionException {
6 if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
7 return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
8 }
9 else {
10 TransactionStatus status = this.transactionManager.getTransaction(this);
11 T result;
12 try {
13 result = action.doInTransaction(status);
14 }
15 catch (RuntimeException ex) {
16 // Transactional code threw application exception -> rollback
17 rollbackOnException(status, ex);
18 throw ex;
19 }
20 catch (Error err) {
21 // Transactional code threw error -> rollback
22 rollbackOnException(status, err);
23 throw err;
24 }
25 catch (Exception ex) {
26 // Transactional code threw unexpected exception -> rollback
27 rollbackOnException(status, ex);
28 throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
29 }
30 this.transactionManager.commit(status);
31 return result;
32 }
33 }
由上面的代碼可以推測到, 真正執行業務方法的關鍵代碼是: action.doInTransaction(status);
正好, 有個入參TransactionCallback<T>, 翻看該接口的源碼:
1 public interface TransactionCallback<T> {
2
5 T doInTransaction(TransactionStatus status);
6
7 }
該接口只有一個doInTransaction方法, 那么很簡單, 我們可以通過匿名內部類的方式將業務代碼放在doInTransaction中:
舉例如下:
1 private PayOrderDAO payOrderDAO;
2
3 protected TransactionTemplate transactionTemplate;
4
5 /**
6 * 保存支付訂單
7 */
8 protected PayOrder savePayReq(final PayOrder payOrder) {
9
10 @Autowired
11 private TransactionTemplate transactionTemplate;
12
13 @Autowired
14 private PayOrderDAO payOrderDAO;
15
16 PayOrder order = (PayOrder) this.transactionTemplate
17 .execute(new TransactionCallback() {
18 @Override
19 public Object doInTransaction(TransactionStatus status) {
20 // 查看是否已經存在支付訂單,如果已經存在則返回訂單主鍵
21 PayOrder payOrderTemp = payOrderDAO.findOrder(String
22 .valueOf(payOrder.getPayOrderId()));
23
24 // 由支付渠道類型(PayChannelType)轉換得到交易類型(PayType)
25 if (payOrder.getPayChannelId().equalsIgnoreCase(PAY_CHNL_ACT_BAL)) {// 賬戶余額支付
26 payOrder.setPayType("3");
27 } else if (payOrder.getPayChannelId().equalsIgnoreCase(PAY_CHNL_FAST_PAY)) {// 聯通快捷支付
28 payOrder.setPayType("4");
29 } else {// 網銀網關支付
30 payOrder.setPayType("2");
31 }
32
33 // 比對新的支付金額與原訂單金額是否一致,如不一致則提示錯誤
34 if (payOrderTemp == null) {
35 String orderId = payOrderDAO.save(payOrder);
36 payOrder.setPayOrderId(orderId);
37 return payOrder;
38 } else {
39 return payOrderTemp;
40 }
41 }
42 });
43 if ("2".equals(order.getOrderState())) {// 2:表示支付成功
44 throw new EpaymentBizException(StatusCode.DQSystem.PAY_FAIL,
45 "同一訂單不能重復支付");
46 } else if (payOrder.getPayAmt().longValue() != order.getPayAmt()
47 .longValue()) {
48 throw new EpaymentBizException(StatusCode.DQSystem.PAY_FAIL,
49 "交易金額與原訂單不一致");
50 } else {
51 return payOrder;
52 }
53
54 }

