文章目录
一、Spring实现银行转账案例
1.代码实现
1.1 持久层
- 提供根据账户名查询账户的方法
public Account findAccountByName(String accountName) {
try{
List<Account> accounts = runner.query("select * from account where name = ? ",new BeanListHandler<Account>(Account.class),accountName);
//根据查询结果 做逻辑判断
//1.没有该账户
if (accounts == null || accounts.size() == 0){
return null;
}
if (accounts.size() > 1){
throw new RuntimeException("结果集不唯一,数据有问题");
}
return accounts.get(0);
}catch (Exception e) {
throw new RuntimeException(e);
}
}
1.2 业务层
- 提供转账方法
public void transfer(String sourceName, String targetName, Float money) {
//1.根据sourceName 获取转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.根据targetName 获取转入账户
Account target = accountDao.findAccountByName(targetName);
//3.转出账户增加金额 转入账户减少金额
source.setMoney(source.getMoney() - money);
target.setMoney(target.getMoney() + money);
//4. 数据库更新账户余额
accountDao.updateAccount(source);
accountDao.updateAccount(target);
}
1.3测试类
- 测试转账过程
@Test
public void testTransfer(){
//1.获取IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.获取service对象
IAccountService as = ac.getBean("accountService",IAccountService.class);
//3.调用转账方法
as.transfer("aaa", "bbb", 100f);
}
1.3.1结果
- 可以实现转账,但是存在问题,下面分析
2.缺乏事务控制带来的问题
- 上述转账过程看似正确,但是存在巨大的问题,因为没有事务控制,所以如果转账过程发生异常,则会出现问题
2.1问题现象
- 人为制造异常
2.2原因分析
二、添加事务控制
添加事务控制尝试解决上述的问题
1创建获取连接工具类
- 连接工具类,用于从数据源中获取一个连接,然后和线程绑定
- 要注意线程回收时和连接的解绑操作
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
//这个对象只能由Spring来创建
private DataSource dataSource;
//所以提供set方法 来完成后面的数据注入
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* 获取当前线程上的连接
* @return
*/
public Connection getThreadConnection(){
//1.先获取连接
Connection conn = tl.get();
try {
//2.判断当前线程上是否有连接
if (conn == null){
//如果没有则创建 并存入tl中
conn = dataSource.getConnection();
tl.set(conn);
}
//返回当前线程上的连接
return conn