一. 为什么使用ThreadLocal:
在上一篇博文中, 我们通过传递Connection的方式来控制事务, 这种方法可以达到目的, 但让人看的不爽,
如果涉及到调用多个service, 那我是不是还得从controller层传递Connection?
ThreadLocal的用法见上一篇博客, 该类保证一个类的实例变量在各个线程中都有一份单独的拷贝,
从而不会影响其他线程中的实例变量,所以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 commit() throws SQLException {
Connection conn = local.get();
if (conn != null) {
conn.commit();
// 清空threadLocal
local.remove();
}
}
// 关闭连接
public static void close() throws SQLException {
Connection conn = local.get();
if (conn != null) {
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.commit();
} catch (Exception e) {
// 转账失败, 回滚事务
TransactionManager.rollback();
e.printStackTrace();
} finally {
// 关闭连接
TransactionManager.close();
}
}
}
使用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层的代码变简洁了.
源码下载: http://download.csdn.net/detail/zdp072/7908261