11.传统事务解决转账问题
步骤分析:
- 编写线程绑定工具类
- 编写事务管理器
- 修改service层代码
- 修改dao层代码
1.编写线程绑定工具类
-
ThreadLocal:线程局部变量
作用:线程使用变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本。
/**
* 连接工具类,从数据源中获取一个连接,并将实现和线程的绑定
*/
@Component
public class ConnectionUtils {
// ThreadLocal:线程局部变量
// 作用:线程使用变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
@Autowired
private DataSource dataSource;
/**
* 获取当前线程是否有连接
*/
public Connection getThreadConnection(){
//1.先从ThreadLocal获取
Connection connection = threadLocal.get();
// 2.判断当前线程中是否有connection
if (connection == null ){
// 3.从数据源中获取一个连接,并且存入ThreadLocal中
try {
connection = dataSource.getConnection();
threadLocal.set(connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return connection;
}
/**
* 解除当前线程的连接绑定
*/
public void removeThredConnection(){
threadLocal.remove();
}
}
2.编写事务管理器
/**
* 事务管理器工具类,包含:开启事务、提交事务、回滚事务、释放资源
*/
@Component
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
// 设置事务为手动提交
public void beginTransacation(){
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 提交事务
public void conmmit(){
try {
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 回滚事务
public void rollback(){
try {
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 释放资源
public void release(){
try {
connectionUtils.getThreadConnection().setAutoCommit(true);//设置会自动提交事务
connectionUtils.getThreadConnection().close();//关闭连接
connectionUtils.removeThredConnection();//接触线程绑定
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3. 修改Service层代码
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Autowired //依赖注入:AccountService a = new AccountServiceImpl(TransactionManager transactionManager)
private TransactionManager transactionManager;
@Override
public void transfer(String name1, String name2,Double money) {
try {
//1.开启事务
transactionManager.beginTransacation();
// 2.业务操作
//一个账号转出资金
accountDao.out(name1,money);
int num = 100;
num = num / 0;
//另一个账号资金就增加
accountDao.in(name2 ,money );
//3、提交事务
transactionManager.conmmit();
}catch (Exception e){
e.printStackTrace();
//4.回滚事务
transactionManager.rollback();
}finally {
//5.释放资源
transactionManager.release();
}
}
}
4.修改dao层代码
@Repository("accountDao") //使用在dao层实例化bean对象;()里不写默认是该类的名字,首字母小写accountDaoImpl
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner queryRunner;
@Autowired
private ConnectionUtils connectionUtils;
@Override
public void out(String outUser, Double money) {
String sql = "update account set money = money - ? where name = ?";
try {
queryRunner.update(connectionUtils.getThreadConnection(), sql,money,outUser);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void in(String outUser, Double money) {
String sql = " update account set money = money + ? where name = ? ";
try {
queryRunner.update(connectionUtils.getThreadConnection(),sql,money,outUser);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
5.结果
当出现异常时,资金并不会减少:
6.问题分析
上面代码,通过对业务层改造,已经可以实现事务控制了,但是由于我们添加了事务控制,也产生了一个新的问题: 业务层方法变得臃肿了,里面充斥着很多重复代码。并且业务层方法和事务控制方法耦合了,违背了面向对象的开发思想。