07.银行转账案例
2 案例中添加转账方法并演示事务问题
3 分析事务的问题并编写ConnectionUtils、4 编写事务管理工具类并分析连接和线程解绑
5 编写业务层和持久层事务控制代码并配置spring的ioc、6 测试转账并分析案例中的问题
7 代理的分析、8 基于接口的动态代理回顾、9 基于子类的动态代理、10 使用动态代理实现事务控制
代码
day02_eesy_02account_xmlioc
day03_eesy_01account
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.15.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.15.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2 案例中添加转账方法并演示事务问题
IAccountService
/**
* 转账
* @param sourceName 转出账户名称
* @param targetName 转入账户名称
* @param money 转账金额
*/
void transfer(String sourceName,String targetName,Float money);
AccountServiceImpl
public void transfer(String sourceName, String targetName, Float money) {
//1.根据名称查询转出账户 ->DAO层需要一个方法实现查询功能
//2.根据名称查询转入账户
//3.转出账户减钱
//4.转入账户加钱
//5.更新转出账户
//6.更新转入账户
}
IAccountDao
/**
* 根据账户名称查询账户所有信息
* @param accountName
* @return 如果有唯一的一个结果就返回,如果没有结果就返回null
* 如果结果集超过一个就抛异常
*/
Account findAccountByName(String accountName);
AccountDaoImpl
/**
* 根据账户名称查询账户所有信息
*
* @param accountName
* @return 如果有唯一的一个结果就返回,如果没有结果就返回null
* 如果结果集超过一个就抛异常
*/
public Account findAccountByName(String accountName) {
try{
List<Account> accounts = runner.query("select * from account where name = ? ",new BeanListHandler<Account> (Account.class),accountName);
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);
}
}
Service层
AccountServiceImpl
/**
* 转账
* @param sourceName 转出账户名称
* @param targetName 转入账户名称
* @param money 转账金额
*/
public void transfer(String sourceName, String targetName, Float money) {
//1.根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//3.转出账户减钱
Float decrease = (source.getMoney()- money);
source.setMoney(decrease);
//4.转入账户加钱
Float increase = (target.getMoney()+ money);
target.setMoney(increase);
//5.更新转出账户
accountDao.updateAccount(source);
//6.更新转入账户
accountDao.updateAccount(target);
}
测试类
AccountServiceTest
/**
* 使用Junit单元测试:测试我们的配置
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
@Autowired
private IAccountService as;
@Test
public void testTransfer(){
as.transfer("aaa","bbb",100f);
}
}
在AccountServiceImpl执行过程中抛异常,会导致减钱事务提交,可是加钱事务未提交。应该如何解决呢?
事务控制应该都是在业务层
ConnectionUtils
package com.itheima.utils;
import javax.sql.DataSource;
import java.sql.Connection;
/**
* 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定
*/
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* 获取当前线程上的连接
*
* @return
*/
public Connection getThreadConnection() {
try {
//1.先从ThreadLocal上获取
Connection conn = tl.get();
//2.判断当前线程上是否有连接
if (conn == null) {
//3.从数据源中获取一个连接,并且存入ThreadLocal中
conn = dataSource.getConnection();
//
tl.set(conn);
}
//4.返回当前线程上的连接
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 把连接和线程解绑
*/
public void removeConnection(){
tl.remove();
}
}
4 编写事务管理工具类并分析连接和线程解绑
TransactionManager
package com.itheima.utils;
/**
* 和事务管理相关的工具类,它包含了:开启事务,提交事务,回滚事务和释放事务
*/
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUti