Java事务(三) - 使用ThreadLocal

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

Java事务(三) - 使用ThreadLocal的相关文章

java事务(三)

java事务(三)--自己实现分布式事务 在上一篇<java事务(二)--本地事务>中已经提到了事务的类型,并对本地事务做了说明.而分布式事务是跨越多个数据源来对数据来进行访问和更新,在JAVA中是使用JTA(Java Transaction API)来实现分布式的事务管理的.但是在本篇中并不会说明如何使用JTA,而是在不依赖其他框架以及jar包的情况下自己来实现分布式事务,作为对分布式事务的一个理解. 假设现在有两个数据库,可以是在一台机器上也可以是在不同机器上,现在要向其中一个数据库更新用

【Java技术点滴】——ThreadLocal封装JDBC事务操作

背景 在Java程序实现中,我们往往应用到事务的机制,在业务层进行事务开启,创建数据库连接,调用Dao层方法进行数据库访问,过程中需要将数据库连接Connection作为参数传递给Dao层方法.显而易见,这样的实现不利于Dao层方法的复用,当在不使用事务的情况下,我们是需要在Dao层方法中创建数据库连接的,这样Dao层方法免去Connection参数就可以使得方法更加独立.明确了,怎样解决这样的尴尬?对于此,我们使用了ThreadLocal进行解决. 基本介绍 "本地线程变量",可以理

java事务(三)——自己实现分布式事务

在上一篇<java事务(二)——本地事务>中已经提到了事务的类型,并对本地事务做了说明.而分布式事务是跨越多个数据源来对数据来进行访问和更新,在JAVA中是使用JTA(Java Transaction API)来实现分布式的事务管理的.但是在本篇中并不会说明如何使用JTA,而是在不依赖其他框架以及jar包的情况下自己来实现分布式事务,作为对分布式事务的一个理解. 假设现在有两个数据库,可以是在一台机器上也可以是在不同机器上,现在要向其中一个数据库更新用户账户信息,另外一个数据库新增用户的消费信

java事务学习笔记(九)--深度剖析JTA原理与实现

通过本系列对java事务的学习,对事务的概念有了初步的了解,但是互联网的发展一日千里,数据量更是爆炸性增长,而普 通数据库也越来越成为应用系统的性能瓶颈,分布式数据库应运而生,相应的,java分布式事务JTA(Java Transaction API)也在这 种背景下产生了.有幸拜读了IBM developersWorks深度好文,加上自己的一些理解分享给各位看官,仅供大家互相交流学习. 原文地址:http://www.ibm.com/developerworks/cn/java/j-lo-jt

Java多线程10:ThreadLocal的作用及使用

ThreadLocal的作用 从上一篇对于ThreadLocal的分析来看,可以得出结论:ThreadLocal不是用来解决共享对象的多线程访问问题的,通过ThreadLocal的set()方法设置到线程的ThreadLocal.ThreadLocalMap里的是是线程自己要存储的对象,其他线程不需要去访问,也是访问不到的.各个线程中的ThreadLocal.ThreadLocalMap以及ThreadLocal.ThreadLocal中的值都是不同的对象. 至于为什么要使用ThreadLoca

Java事务与JTA

一.什么是JAVA事务 通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令.更简答的说就是:要么全部执行成功,要么撤销不执行. 事务必须服从ISO/IEC所制定的ACID原则. 原子性(atomicity) 一致性(consistency) 隔离性(isolation) 持久性(durability) 原子性表示事务执行过程中的任何失败都将导致事务所做的任何修改失效. 一致性表示当事务执行失败

【Java】深入理解ThreadLocal

一.前言 要理解ThreadLocal,首先必须理解线程安全.线程可以看做是一个具有一定独立功能的处理过程,它是比进程更细度的单位.当程序以单线程运行的时候,我们不需要考虑线程安全.然而当一个进程中包含多个线程的时候,就需要考虑线程安全问题,因为此时线程可能会同时操作同一个资源,当两个或者两个以上线程同时操作一个资源的时候,就会造成冲突.不一致等问题,即线程不安全. 解决线程安全问题,本质上就是解决资源共享问题,一般有以下手段: 1)可重入(不依赖环境):2)互斥(同一时间段只允许一个线程使用)

温故而知新java事务

一.什么是Java事务 通常的观念认为,事务仅与数据库相关. 事务必须服从ISO/IEC所制定的ACID原则.ACID是原子性(atomicity).一致性(consistency).隔离性 (isolation)和持久性(durability)的缩写. 原子性:表示事务执行过程中的任何失败都将导致事务所做的任何修改失效. 一致性:表示 当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态. 隔离性:表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见. 持 久性:表

java事务的类型——面试被问到

Java事务的类型有三种:JDBC事务.JTA(Java Transaction API)事务.容器事务. 1.JDBC事务 JDBC 事务是用 Connection 对象控制的.JDBC Connection 接口( java.sql.Connection )提供了两种事务模式:自动提交和手工提交. java.sql.Connection 提供了以下控制事务的方法: public void setAutoCommit(boolean)public boolean getAutoCommit()