深入解析ThreadLocal( 四 )

所以这里就需要操作事务 , 来保证转出和转入操作具备原子性 , 要么同时成功 , 要么同时失败 。
(1) JDBC中关于事务的操作的api
-node="block" data-draft-type="table" data-size="normal" data-row-style="normal">
Connection接口的方法作用
(2) 开启事务的注意点:

  • 为了保证所有的操作在一个事务中,案例中使用的连接必须是同一个: service层开启事务的connection需要跟dao层访问数据库的connection保持一致
  • 线程并发情况下, 每个线程只能操作各自的 connection
2.2 常规解决方案2.2.1 常规方案的实现基于上面给出的前提 ,大家通常想到的解决方案是 :
  • 传参: 从service层将connection对象向dao层传递
  • 加锁
以下是代码实现修改的部分:
(1 ) AccountService 类
package com.itheima.transfer.service;import com.itheima.transfer.dao.AccountDao;import com.itheima.transfer.utils.JdbcUtils;import java.sql.Connection;public class AccountService {public boolean transfer(String outUser, String inUser, int money) {AccountDao ad = new AccountDao();//线程并发情况下,为了保证每个线程使用各自的connection,故加锁synchronized (AccountService.class) {Connection conn = null;try {conn = JdbcUtils.getConnection();//开启事务conn.setAutoCommit(false);// 转出ad.out(conn, outUser, money);// 模拟转账过程中的异常//int i = 1/0;// 转入ad.in(conn, inUser, money);//事务提交JdbcUtils.commitAndClose(conn);} catch (Exception e) {e.printStackTrace();//事务回滚JdbcUtils.rollbackAndClose(conn);return false;}return true;}}}(2) AccountDao 类 (这里需要注意的是: connection不能在dao层释放 , 要在service层 , 不然在dao层释放 , service层就无法使用了)
package com.itheima.transfer.dao;import com.itheima.transfer.utils.JdbcUtils;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class AccountDao {public void out(Connection conn, String outUser, int money) throws SQLException{String sql = "update account set money = money - ? where name = ?";//注释从连接池获取连接的代码,使用从service中传递过来的connection//Connection conn = JdbcUtils.getConnection();PreparedStatement pstm = conn.prepareStatement(sql);pstm.setInt(1,money);pstm.setString(2,outUser);pstm.executeUpdate();//连接不能在这里释放,service层中还需要使用//JdbcUtils.release(pstm,conn);JdbcUtils.release(pstm);}public void in(Connection conn, String inUser, int money) throws SQLException {String sql = "update account set money = money + ? where name = ?";//Connection conn = JdbcUtils.getConnection();PreparedStatement pstm = conn.prepareStatement(sql);pstm.setInt(1,money);pstm.setString(2,inUser);pstm.executeUpdate();//JdbcUtils.release(pstm,conn);JdbcUtils.release(pstm);}}2.2.2 常规方案的弊端上述方式我们看到的确按要求解决了问题 , 但是仔细观察 , 会发现这样实现的弊端:
  1. 直接从service层传递connection到dao层, 造成代码耦合度提高
  2. 加锁会造成线程失去并发性 , 程序性能降低
2.3 ThreadLocal解决方案2.3.1 ThreadLocal方案的实现像这种需要在项目中进行数据传递和线程隔离的场景 , 我们不妨用ThreadLocal来解决:
(1) 工具类的修改: 加入ThreadLocal


推荐阅读