通过实例讲解java中具备全程事务控制的JDBC连接管理器知识_java 

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

  [java]

  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),这样可以保证为每一个线程存储单个不同的连接对象

  [java]

  // 线程本地对象管理器

  private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();// 数据源private DataSource dataSource;

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

  [java]

  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接口定义如下:

  [java]

  package edu.softparty.base.dbunit;

  import java.sql.SQLException;

  public interface Transaction {

  void commit() throws SQLException;

  void rollback() throws SQLException;

  }

  该接口在ConnectionManager类中被实现如下:

  实现一:

  [java]

  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();

  }

  }

  };

  实现一中,内嵌类通过其外部类ConnectionManager的线程本地存储对象获取数据库连接对象,如能正确获取且连接对象上启动了事务,则进行事务的回滚或提交,并在操作完毕后关闭数据库连接并将连接对象从线程本地存储对象中删除,防止该连接被再次获取和使用。

  实现二:

  [java]

  private static final Transaction EMPTY_TRANSACTION = new Transaction() {

  public void rollback() throws SQLException {

  }

  public void commit() throws SQLException {

  }

  };

  实现二是一个伪实现,其目的就是为了什么也不做,这样一来就可以在ConnectionManager类的getTransaction方法中获取一个合适的Transaction接口对象

  [java]

  public Transaction getTransaction() {

  return connectionHolder.get() == null ? EMPTY_TRANSACTION : TRANSACTION;

  }

  即在获取事务对象时,如果数据库连接存在,则返回可以操作事务的Transaction对象,否则返回伪实现对象,以保证返回的结果上可以正确调用rollback和commit方法。

  在数据库链接使用完毕后,可以通过提交/回滚事务或者close方法对连接进行释放,连接一旦关闭则将连接对象从本地线程存储中移除,再次调用getConnection方法时又会获取一个新的数据库连接对象。

  [java]

  public void close() throws SQLException {

  Connection connection = connectionHolder.get();

  if (connection != null) {

  connection.close();

  connectionHolder.remove();

  }

  }

  通过这个连接管理器,可以保证在同一线程的任意方法中获取连接、启动事务,在其它方法中都可以进行事务的回滚、提交或连接关闭操作。

  配

  package edu.softparty.base.dbunit;

  import javax.naming.InitialContext;

  import javax.naming.NamingException;

  import javax.sql.DataSource;

  import org.apache.tomcat.dbcp.dbcp.BasicDataSource;

  /**

  * 数据连接工厂类

  */

  public class ConnectionFactory {

  // 单例工厂类对象

  private static ConnectionManager connectionManager = null;

  /**

  * 私有构造器

  */

  private ConnectionFactory() {

  }

  /**

  * 创建连接管理器对象

  * @return 连接管理器对象

  */

  public static ConnectionManager getConnectionManager() {

  if (connectionManager == null) {

  try {

  DataSource dataSource = (DataSource) new InitialContext()。lookup("java:comp/env/jdbc/mvc");

  connectionManager = new ConnectionManager(dataSource);

  } catch (NamingException e) {

  e.printStackTrace();

  }

  }

  return connectionManager;

  }

  /**

  * 为测试初始化连接环境

  */

  public static void initializeForTesting(String driverClassName, String url, String username, String password) {

  if (connectionManager == null) {

  BasicDataSource dataSource = new BasicDataSource();

  dataSource.setDriverClassName(driverClassName);

  dataSource.setUrl(url);

  dataSource.setUsername(username);

  dataSource.setPassword(password);

  connectionManager = new ConnectionManager(dataSource);

  }

  }

  }

DAO中可以通过ConnectionManager类完成连接对象的获取

  [java]

  package edu.softparty.mvc.dao;

  import java.sql.PreparedStatement;

  import java.sql.ResultSet;

  import java.sql.SQLException;

  import edu.softparty.base.dao.RecordDAO;

  import edu.softparty.base.dbunit.ConnectionFactory;

  import edu.softparty.base.dbunit.ConnectionManager;

  public class RecordDAOImpl implements RecordDAO {

  // 连接管理器

  private static final ConnectionManager manager = ConnectionFactory.getConnectionManager();

  /**

  * @see edu.softparty.base.dao.RecordDAO#getCount()

  */

  public int getCount() throws SQLException {

  final String sql = "select count(*) from mvc_demo";

  ResultSet rs = null;

  PreparedStatement stm = null;

  try {

  stm = manager.getConnection()。prepareStatement(sql);

  rs = stm.executeQuery();

  rs.next();

  return rs.getInt(1);

  } finally {

  manager.release(rs, stm);

  }

  }

  // …… 其它DAO操作

  }

  实现一个Servlet超类,即可对事务进行全程控制,在业务代码中只需要调用一次ConnectionManager类的beginTransaction方法启动事务即可

  [java]

  package edu.softparty.base.servlet;

  import java.io.IOException;

  import java.sql.SQLException;

  import javax.servlet.ServletException;

  import javax.servlet.http.HttpServlet;

  import javax.servlet.http.HttpServletRequest;

  import javax.servlet.http.HttpServletResponse;

  import edu.softparty.base.bo.Result;

  import edu.softparty.base.dbunit.ConnectionFactory;

  import edu.softparty.base.dbunit.ConnectionManager;

  /**

  *

  */

  public abstract class ServletSupport extends HttpServlet {

  private static final long serialVersionUID = 1L;

  /**

  * 执行具体业务的方法

  * @param request

  * @param response

  * @throws ServletException

  * @throws IOException

  */

  public abstract Result execute(HttpServletRequest request, HttpServletResponse response) throws Exception;

  /**

  * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)

  */

  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  doPost(request, response);

  }

  /**

  * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)

  */

  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  // 获取连接管理器对象

  ConnectionManager manager = ConnectionFactory.getConnectionManager();

  try {

  Result result = null;

  try {

  // 执行业务流程代码

  result = execute(request, response);

  // 提交事务

  manager.getTransaction()。commit();

  } catch (SQLException e) {

  // 回滚事务

  manager.getTransaction()。rollback();

  throw e;

  }

  result.forward(request, response);

  } catch (Exception e) {

  e.printStackTrace();

  throw new ServletException(e);

  }

  }

  }

  如此以来,就完成了最基本的全程连接管理和事务控制,在一些不便于使用开源框架的环境里,用起来还是比较省心舒适的!