在基于数据库的应用中,事务是非常重要的。为了方便使用,Spring提供了基于XML和基于注解的方式配置事务,思路都是使用AOP,在特定的切入点统一开启事务,以方法为粒度进行事务控制。并且定义了事务的传播属性,规定了配置了事务的方法互相嵌套调用时的行为准则:
- PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED:支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。
越是统一的东西,灵活性就越差。事务是业务逻辑的一部分,有时候事务的开启并不能以方法为粒度进行统一控制,这时候很多开发人员的做法是"将就"基于AOP的事务配置的方法,将需要开启事务的逻辑单独拆出方法进行控制,这其实是一种妥协,而且有时候并不见得称心如意。那么Spring除了基于AOP,还有别的方式管理事务吗?答案就是org.springframework.transaction.support.TransactionTemplate。下面我们研究下它的使用。首先看下类图:
TransactionTemplate继承了DefaultTransactionDefinition,它是TransactionDefinition的一个实现,TransactionDefinition是Spring事务管理中很重要的一个概念,它是事务配置的入口,可以配置事务的各种属性,如隔离级别、传播属性、超时时间、是否只读,通过实现这个接口,TransactionTemplate具备了配置事务的能力。另外必须为它指定transactionManager,毕竟它只是负责触发事务的开启,并不具备事务管理的能力:
com.cuilei01.mgr.utils
org.hibernate.dialect.MySQL5Dialect
${hibernate.show_sql}
com.mysql.jdbc.Driver
${jdbc.mgr.url}
${jdbc.mgr.user}
${jdbc.mgr.password}
现在就可以在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() {
@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
观察下事务的执行情况,事务work了。
看下它的核心代码:
@Override
public T execute(TransactionCallback 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;
}
}
也可以看到,TransactionTemplate和基于AOP的配置一样,也是在方法前后执行事务的开启和提交,只是实现的方式改变了。