Java事务(三) - 使用ThreadLocal

一. 为什么使用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


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值