【菜鸟学Java】14:使用ThreadLocal对Connection进行封装

JDBC连接池与ThreadLocal
本文探讨了在复杂的业务场景下,如何确保数据库操作的原子性和一致性。通过使用ThreadLocal来管理每个线程内的数据库连接,避免了多次连接带来的问题,并提供了具体的实现代码。

        问题背景:

        使用JDBC进行开发的时候,每一次的增删改查都必须和数据库建立连接,才可以对数据项进行相应的操作。当我们的业务比较复杂的情况下,可能会出现在一个方法中多次的执行增删改查,这样的话,在这个方法的执行过程中,就需要与数据库建立多次的连接,在这种场景中,如何保证在并发执行这个方法的过程中,与数据库的连接不会混乱,保证这些操作的原子性,就显得尤为重要了。如何解决这个问题呢?

        问题分析:

我能想到的方案有两个,其一:在这个多次执行增删改查的方法内,声明一个局部变量用于存放数据库连接对象Connection,这样在调用增删改查方法的时候,将Connection对象作为参数传进去,这样就保证了这些子操作都使用的是同一个连接,从而保证了所有操作的连续性和原子性,不会出现数据库连接对象混乱使用的情况。

      其二:我们仔细想想,其实用户的每一次请求,都会调用程序相应的Servlet,而Servlet是单实例多线程的,也就是说每一次的请求程序都启动一个线程为用户服务,在这个线程中会调用很多的方法,包括我们上面提到的那个需要和数据库进行多次连接的复杂方法,由此我们可以这样想,只要保证在这个线程中,我们所使用的数据库连接对象Connection都是同一个,一样可以保证这些操作的连续性和原子性。

        相比较第一种方案而言,第二种显然要更好一些,因为第一种需要我们在编写增删改查方法时,定义Connection的参数,第二种则不用,直接将Connection进行一下封装即可。

        问题解决:

        JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。我们就使用ThreadLocal这个类来对Connection进行一下简单的封装,以满足我们的需求。代码如下:

/**
 * 采用ThreadLocal封装Connection
 * @author ljw
 *
 */
public class ConnectionManager {

	//声明一个本地线程变量,用于存放connection
	private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();
	
	/**
	 * 得到Connection
	 * @return
	 */
	public static Connection getConnection() {
		Connection conn = connectionHolder.get();
		//如果在当前线程中没有绑定相应的Connection
		if (conn == null) {
			try {
				JdbcConfig jdbcConfig = XmlConfigReader.getInstance().getJdbcConfig();
				Class.forName(jdbcConfig.getDriverName());
				conn = DriverManager.getConnection(jdbcConfig.getUrl(), jdbcConfig.getUserName(), jdbcConfig.getPassword());
				//将Connection设置到线程变量ThreadLocal中
				connectionHolder.set(conn);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
				throw new ApplicationException("系统错误,请联系系统管理员");
			} catch (SQLException e) {
				e.printStackTrace();
				throw new ApplicationException("系统错误,请联系系统管理员");
			}
		}
		return conn;
	}
	/**
	 * 关闭连接,只关闭当前线程的连接
	 */
	public static void closeConnection() {
		Connection conn = connectionHolder.get();
		if (conn != null) {
			try {
				conn.close();
				//从ThreadLocal中清除Connection
				connectionHolder.remove();
			} catch (SQLException e) {
				e.printStackTrace();
			}	
		}
	}
	
	public static void close(Connection conn) {
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void close(Statement pstmt) {
		if (pstmt != null) {
			try {
				pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void close(ResultSet rs ) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void beginTransaction(Connection conn) {
		try {
			if (conn != null) {
				if (conn.getAutoCommit()) {
					conn.setAutoCommit(false); //手动提交
				}
			}
		}catch(SQLException e) {}
	}
	
	public static void commitTransaction(Connection conn) {
		try {
			if (conn != null) {
				if (!conn.getAutoCommit()) {
					conn.commit();
				}
			}
		}catch(SQLException e) {}
	}
	
	public static void rollbackTransaction(Connection conn) {
		try {
			if (conn != null) {
				if (!conn.getAutoCommit()) {
					conn.rollback();
				}
			}
		}catch(SQLException e) {}
	}

}

        小结一下:

        我们可以在方法内部进行控制,当然我们也可以在线程中进行控制,同一个线程的局部变量不会影响到另一个线程的,Java为我们提供了丰富的类库去开发各种各样的程序,把玩这些类可以窥探程序设计乐趣,这就是编程的魅力所在。在编程中多线程的问题很值得研究,本篇文章中只是解决了一个小小的Connection独立性的问题,还有很多很多的其他问题,需要我们去认识和理解。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值