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);
}
}
}
如此以來,就完成了最基本的全程鏈接管理和事務控制,在一些不便於使用開源框架的環境裏,用起來仍是比較省心溫馨的!