二、编程式事务xml的配置
基于 TransactionTemplate 的编程式事务管理
现在就可以在Spring管理的Bean中注入并使用:
- @ContextConfiguration(locations = {"classpath:applicationContext.xml"})
- @RunWith(SpringJUnit4ClassRunner.class)
- public class TransactionTest {
- private final Logger logger = LoggerFactory.getLogger(TransactionTest.class);
- @Resource
- private TransactionTemplate transactionTemplate;
- @Test
- public void testProgrammaticTransaction() {
- logger.info("Begin test programmatic transaction!########################");
- // 第一个事务
- Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() {
- @Override
- public Integer doInTransaction(TransactionStatus status) {
- logger.info("Do in transaction with a return value!#####################################");
- // 在事务中执行, 有返回值
- return 1;
- }
- });
- logger.info("result:{}", result);
- // 第二个事务
- transactionTemplate.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- logger.info("Do in transaction without a return value!#####################################");
- // 在事务中执行,没有返回值
- }
- });
- }
看到TransactionTemplate的使用比较简单,只需将需要在事务中之行的逻辑封装成TransactionCallback<T>,这个是带返回值的,不带返回值的封装成TransactionCallbackWithoutResult。
观察下事务的执行情况,事务work了。
看下它的核心代码:
- @Override
- public <T> T execute(TransactionCallback<T> action) throws TransactionException {
- if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
- return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
- }
- else {
- TransactionStatus status = this.transactionManager.getTransaction(this);
- T result;
- try {
- result = action.doInTransaction(status);
- }
- catch (RuntimeException ex) {
- // Transactional code threw application exception -> rollback
- rollbackOnException(status, ex);
- throw ex;
- }
- catch (Error err) {
- // Transactional code threw error -> rollback
- rollbackOnException(status, err);
- throw err;
- }
- catch (Exception ex) {
- // Transactional code threw unexpected exception -> rollback
- rollbackOnException(status, ex);
- throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
- }
- this.transactionManager.commit(status);
- return result;
- }
- }
需要执行事务的业务逻辑被封装成action,它所做的也很简单,在action执行前后进行事务的开启和提交(或着rollback)。开启事务时需要transactionManager的getTransaction方法获取TransactionStatus,而这个方法的参数是TransactionDefinition,前面说过TransactionTemplate本身就是TransactionDefinition的实现,所以将this作为参数传递给这个方法就可以。
也可以看到,TransactionTemplate和基于AOP的配置一样,也是在方法前后执行事务的开启和提交,只是实现的方式改变了。
TransactionTemplate 的 execute() 方法有一个 TransactionCallback 类型的参数,该接口中定义了一个 doInTransaction() 方法,通常我们以匿名内部类的方式实现 TransactionCallback 接口,并在其 doInTransaction() 方法中书写业务逻辑代码。这里可以使用默认的事务提交和回滚规则,这样在业务代码中就不需要显式调用任何事务管理的 API。doInTransaction() 方法有一个TransactionStatus 类型的参数,我们可以在方法的任何位置调用该参数的 setRollbackOnly() 方法将事务标识为回滚的,以执行事务回滚。
根据默认规则,如果在执行回调方法的过程中抛出了未检查异常,或者显式调用了TransacationStatus.setRollbackOnly() 方法,则回滚事务;如果事务执行完成或者抛出了 checked 类型的异常,则提交事务。
TransactionCallback 接口有一个子接口 TransactionCallbackWithoutResult,该接口中定义了一个 doInTransactionWithoutResult() 方法,TransactionCallbackWithoutResult 接口主要用于事务过程中不需要返回值的情况。当然,对于不需要返回值的情况,我们仍然可以使用 TransactionCallback 接口,并在方法中返回任意值即可。
基于底层 API 的编程式事务管理
根据PlatformTransactionManager、TransactionDefinition 和 TransactionStatus 三个核心接口,我们完全可以通过编程的方式来进行事务管理。示例代码如清单4所示:
@Autowired
private PlatformTransactionManager transactionManager;
// 此处启用编程式事务,是为了保证try里的数据一致
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
/**
* 如果使用第三方支付的同时还使用了钱包的某项补贴消费了,要去钱包扣减这项补贴,并且添加相应的补贴流水
*/
String orderIdPrefix = CfgHelper.getValue(Constants.orderId_prefix);
log.error("paymentserviceimpl l来到这里了========================orderIdPrefix"+orderIdPrefix);
if (null != order.getTags() && order.getTags().trim().equals("CONSUME_SUBSIDY")) {
log.info("处理本次订单的交易的服务为:" + orderIdPrefix + ",订单号:" + orderId + ",处理时间:" + System.currentTimeMillis());
log.error("处理本次订单的交易的服务为:" + orderIdPrefix + ",订单号:" + orderId + ",处理时间:" + System.currentTimeMillis());
newWalletPayment(order.getUserId(), orderId, order.getDeductionPrice(), PaymentMethodEnum.CONSUME.getValue());
} else if (null != order.getTags() && order.getTags().trim().equals("HT_SUBSIDY")) {
log.info("处理本次订单的交易的服务为:" + orderIdPrefix + ",订单号:" + orderId + ",处理时间:" + System.currentTimeMillis());
newWalletPayment(order.getUserId(), orderId, order.getDeductionPrice(), PaymentMethodEnum.HTSUBSIDY.getValue());
log.error("处理本次订单的交易的服务为:" + orderIdPrefix + ",订单号:" + orderId + ",处理时间:" + System.currentTimeMillis());
}
log.error("更新订单状态即将=======:" + order + ",order.getDmId()号:" + order.getDmId() + ",payType时间:" + payType+"threeSerialNumber"+threeSerialNumber);
rechargeSeller(order, order.getDmId() + "", payType, threeSerialNumber);
log.error("回来了============================================");
transactionManager.commit(status);
} catch (Exception e) {
e.printStackTrace();
log.error("用户:" + order.getUserId() + ",订单:" + orderId + ",抛出异常,事务回滚." + e.getMessage());
transactionManager.rollback(status);
throw new ServiceException("用户:" + order.getUserId() + ",订单:" + orderId + "修改钱包和添加补贴流水异常." + e.getMessage(), e);
}
如上所示,我们在类中增加了两个属性:一个是 TransactionDefinition 类型的属性,它用于定义一个事务;另一个是 PlatformTransactionManager 类型的属性,用于执行事务管理操作。
如果方法需要实施事务管理,我们首先需要在方法开始执行前启动一个事务,调用PlatformTransactionManager.getTransaction(...) 方法便可启动一个事务。创建并启动了事务之后,便可以开始编写业务逻辑代码,然后在适当的地方执行事务的提交或者回滚。