日期:2014-05-16  浏览次数:20623 次

[Java]具备全程事务控制的JDBC连接管理器

  Hibernate事务具备全局管理能力,配合Spring框架就可以在BO层完成DAO操作和事务控制。当然,传统的JDBC是不具备这个能力的,所以要自己开发一个连接管理器框架,来管理线程范围内的数据库连接和事务控制。

package edu.softparty.base.dbunit;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

/**
 * 连接管理器类
 */
public class ConnectionManager {

	/**
	 * 空的事务对象
	 */
	private static final Transaction EMPTY_TRANSACTION = new Transaction() {
		public void rollback() throws SQLException {
		}
		public void commit() throws SQLException {
		}
	};

	/**
	 * 负责提交和回滚的事务对象
	 */
	private static final Transaction TRANSACTION = new Transaction() {
		public void rollback() throws SQLException {
			Connection connection = connectionHolder.get();
			if (connection != null) {
				if (connection.getAutoCommit() == false) {
					connection.rollback();
				}
				connection.close();
				connectionHolder.remove();
			}
		}
		
		public void commit() throws SQLException {
			Connection connection = connectionHolder.get();
			if (connection != null) {
				if (connection.getAutoCommit() == false) {
					connection.commit();
				}
				connection.close();
				connectionHolder.remove();
			}
		}
	};


	// 线程本地对象管理器
	private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();

	// 数据源
	private DataSource dataSource;

	/**
	 * 构造器
	 * @param dataSource 数据源对象
	 */
	ConnectionManager(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	/**
	 * 获取数据库连接
	 * @return 数据库连接
	 * @throws SQLException
	 */
	public Connection getConnection() throws SQLException {
		Connection connection = connectionHolder.get();
		if (connection == null) {
			connection = dataSource.getConnection();
			connectionHolder.set(connection);
		}
		return connection;
	}

	/**
	 * 启动事务
	 * @return 事务管理对象
	 * @throws SQLException
	 */
	public Transaction beginTransaction() throws SQLException {
		Connection connection = getConnection();
		if (connection.getAutoCommit()) {
			connection.setAutoCommit(false);
		}
		return TRANSACTION;
	}

	/**
	 * 获取事务
	 * @return
	 * @throws SQLException
	 */
	public Transaction getTransaction() {
		return connectionHolder.get() == null ? EMPTY_TRANSACTION : TRANSACTION;
	}

	/**
	 * 关闭数据库连接
	 * @throws SQLException
	 */
	public void close() throws SQLException {
		Connection connection = connectionHolder.get();
		if (connection != null) {
			connection.close();
			connectionHolder.remove();
		}
	}

	/**
	 * 释放资源
	 * @param rs 结果集对象
	 * @param stm 命令集对象
	 * @throws SQLException
	 */
	public void release(ResultSet rs, Statement stm) throws SQLException {
		if (rs != null) {
			rs.close();
		}
		if (stm != null) {
			stm.close();
		}
	}
}

  上面的代码实现了一个连接管理器,该链接管理器主要具备如下功能:
    * 线程范围内的连接管理
    * 线程范围内的事务管理

  对于连接管理,主要使用了Java的线程本地存储(ThreadLocal),这样可以保证为每一个线程存储单个不同的连接对象

// 线程本地对象管理器
 private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();// 数据源private DataSource dataSource;

  数据源对象由构造器来注入,在getConnection方法中,从线程本地存储中获取一个现存的数据库连接对象或者从数据源中建立一个新的数据库连接对象

	public Connection getConnection() throws SQLException {
		Connection connection = connectionHolder.get();
		if (connection == null) {
			connection = dataSource.getConnection();
			connectionHolder.set(connection);
		}
		return connection;
	}
  这样,只要数据库连接不被关闭,就可以在一个线程内一直获取相同的数据库连接对象
  同样,beginTransaction方法会通过getConnection方法获取一个数据库连接对象,并在其之上启动事务。由此一来,只要不关闭数据库连接对象,则再次调用getConnection方法获取的数据库连接对象,都会存在于相同的事务中。

  beginTransaction方法返回的是一个Transaction接口单例对象,该接口被作为ConnectionManager类的内嵌类来实现,其作用是通过ConnectionManager类中的线程本地存储对象获取之前产生的数据库连接对象。

  Transaction接口定义如下:

package edu.softparty.base.dbunit;

import java.sql.SQLException;

public interfa