Spring 编程式事务控制
# 180.Spring 编程式事务控制
之前我们都是使用声明式的事务控制,通过配置的方式实现注解;现在讲讲如何通过编程式来实现控制,当然只是简单说说,了解即可,实际开发过程中使用的很少。
# 环境准备
本次我们使用之前的案例:Spring 的 事务控制 ,也就是基于 demo17 的分支进行开发
# 开始配置
首先事务控制都离不开提交和回滚两个动作,所以即使是编程式事务控制,也得配置事务管理器
我们在 bean.xml 中添加如下内容:
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
2
3
4
5
Spring 还给我们提供了一个事务模板对象 TransactionTemplate:
<!-- 配置事务模版对象 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"/>
</bean>
2
3
4
5
TransactionTemplate 类中有一个方法叫做 execute:
@Override
@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);
T result;
try {
result = action.doInTransaction(status);
}
catch (RuntimeException | Error ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Throwable 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;
}
}
private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
logger.debug("Initiating transaction rollback on application exception", ex);
try {
this.transactionManager.rollback(status);
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
我们重点看第 13 行,有个 action.doInTransaction
方法,并且执行后,如果有异常,就执行 rollbackOnException 方法回滚;在 rollbackOnException 方法中,执行的就是 transactionManager.rollback(status);
方法,最后执行的也是 connection 对象的 rollbank。
如果没有异常,则执行 transactionManager.commit
方法,也就是提交,最后执行的也是 connection 对象的 commit。
这个 action 是一个参数,其类型是 TransactionCallback
,是一个接口。
接下来我们就在 service 层中配置。
# 在 service 层配置
我们在 service 实现类添加如下成员变量和 set 方法:
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
2
3
4
5
然后配置 bean.xml:注入 transactionTemplate 对象
<bean id="accountService" class="com.peterjxl.service.impl.AccountServiceImpl">
<!-- 注入AccountDao -->
<property name="accountDao" ref="accountDao"/>
<!-- 注入TransactionTemplate -->
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
2
3
4
5
6
然后我们在 transfer 方法中添加如下代码:也就是在模板对象的 execute 方法中执行事务,并且我们传入一个 TransactionCallback
接口的匿名内部类:
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
return null;
}
});
2
3
4
5
6
然后我们将转账的代码中挪到 execute 方法里:
@Override
public void transfer(String sourceName, String targetName, Float money) {
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
// 2.1 根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
// 2.2 根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
// 2.3 转出账户减钱
source.setMoney(source.getMoney() - money);
// 2.4 转入账户加钱
target.setMoney(target.getMoney() + money);
// 2.5 更新转出账户
accountDao.updateAccount(source);
int i = 1/0;
// 2.6 更新转入账户
accountDao.updateAccount(target);
return null;
}
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
此时我们的 transfer 方法,就和模板对象的 execute 方法执行过程很类似。而我们自己的转账的业务逻辑代码,就相当于 execute 方法中,action.doInTransaction
方法内执行的内容。
如果有异常,就会回滚;没有异常才会提交。
然后我们可以测试下能否正常回滚事务,经过测试是可以控制住事务的
# 缺点
综上,我们就是这样配置事务的,也就是 service 中的每个方法,都要这样配置:
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public Account findAccountById(Integer accountId) {
return transactionTemplate.execute(new TransactionCallback<Account>() {
@Override
public Account doInTransaction(TransactionStatus status) {
return accountDao.findAccountById(accountId);
}
});
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
// 2.1 根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
// 2.2 根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
// 2.3 转出账户减钱
source.setMoney(source.getMoney() - money);
// 2.4 转入账户加钱
target.setMoney(target.getMoney() + money);
// 2.5 更新转出账户
accountDao.updateAccount(source);
int i = 1/0;
// 2.6 更新转入账户
accountDao.updateAccount(target);
return null;
}
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
至此,又多了很多重复代码,变的很不灵活
# 源码
本项目已将源码上传到 GitHub (opens new window) 和 Gitee (opens new window) 上。并且创建了分支 demo21,读者可以通过切换分支来查看本文的示例代码