一. 为什么使用ThreadLocal:
在上一篇博文中, 我们通过传递Connection的方式来控制事务, 这种方法可以达到目的, 但让人看的不爽,
如果涉及到调用多个service, 那我是不是还得从controller层传递Connection?
ThreadLocal的用法见上一篇博客, 该类保证一个类的实例变量在各个线程中都有一份单独的拷贝,
从而不会影响其他线程中的实例变量
二. 如何使用ThreadLocal:
1. 写一个TransactionManager类:
/** * 管理事务 */ public class TransactionManager { private static ThreadLocal<Connection> local = new ThreadLocal<Connection>(); // 开启事务 public static void beginTransaction() throws SQLException { Connection conn = JDBCUtils.getConnection(); conn.setAutoCommit(false); // 将连接存入threadLocal local.set(conn); } // 回滚事务 public static void rollback() throws SQLException { Connection conn = local.get(); if (conn != null) { conn.rollback(); conn.close(); // 清空threadLocal local.remove(); } } // 提交事务 public static void commitAndClose() throws SQLException { Connection conn = local.get(); if (conn != null) { conn.commit(); conn.close(); // 清空threadLocal local.remove(); } } // 获取数据库连接 public static Connection getConnection() { return local.get(); } }
使用ThreadLocal, 确保相同的线程获取到的是同一个连接.
2.修改业务处理类
/** * 业务逻辑层 */ public class AccountService { public void transfer(Account outAccount, Account inAccount, int money) throws SQLException { // 开启 事务 TransactionManager.beginTransaction(); // 查询两个账户 AccountDAO accountDAO = new AccountDAO(); outAccount = accountDAO.findAccountById(outAccount.getId()); inAccount = accountDAO.findAccountById(inAccount.getId()); // 转账 - 修改原账户金额 outAccount.setMoney(outAccount.getMoney() - money); inAccount.setMoney(inAccount.getMoney() + money); try { // 更新账户金额 accountDAO.update(outAccount); accountDAO.update(inAccount); // 转账成功, 提交事务 TransactionManager.commitAndClose(); } catch (Exception e) { // 转账失败, 回滚事务 TransactionManager.rollback(); e.printStackTrace(); } } }
使用TransactionManager来管理事务, 代码变得更加简洁.
3. 修改Dao类
/** * DAO层: CRUD */ public class AccountDAO { // 查询账户 public Account findAccountById(int id) throws SQLException { String sql = "select * from account where id = ?"; Object[] params = {id}; QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource()); return queryRunner.query(sql, new BeanHandler<Account>(Account.class), params); } // 更新账户 public void update(Account account) throws SQLException { String sql = "update account set name = ?, money = ? where id = ?"; Object[] params = {account.getName(), account.getMoney(), account.getId()}; // 从threadLocal中获取连接, 同一个线程拿到的是同一个连接 Connection conn = TransactionManager.getConnection(); QueryRunner queryRunner = new QueryRunner(); queryRunner.update(conn, sql, params); } }
不需要传递Connection, 直接从TransactionManager中获取连接.
三. 总结:
service和dao都是通过TransactionManager来获取Connection, 同一个线程中, 它们在整个事务处理过程中使用了相同的Connection对象, 所以事务会处理成功, dao中没有接受和业务无关的对象, 消除了api污染, 另外使用TransactionManager来管理事务, 使service层的代码变简洁了.
时间: 2024-10-18 13:03:31