Hibernate事务具有全局管理能力,配合Spring框架就能够在BO层完成DAO操做和事务控制。固然,传统的JDBC是不具有这个能力的,因此要本身开发一个链接管理器框架,来管理线程范围内的数据库链接和事务控制。java
[java]sql
package edu.softparty.base.dbunit;数据库
import java.sql.Connection;apache
import java.sql.ResultSet;tomcat
import java.sql.SQLException;mvc
import java.sql.Statement;框架
import javax.sql.DataSource;ide
/**测试
* 链接管理器类this
*/
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);
}
}
}
如此以来,就完成了最基本的全程链接管理和事务控制,在一些不便于使用开源框架的环境里,用起来仍是比较省心温馨的!