前幾天在csdn論壇裏面,經常有人問到,如果不用spring,單用hibernate如何來解決延遲加載的問題.
無論是立即加載還是延遲加載必須要連接數據庫的,而在java中連接數據庫是依賴java.sql.Connection,在hibernate中session就是Connection的一層高級封裝,一個session對應了一個Connection,要實現延遲加載必須有session才行.而且要進行延遲加載還必須保證是同一個session才行,用另外一個session去延遲加載前一個session的代理對象是不行的.大家都知道Connection是使用過後必須要進行關閉的,那麼我們如何保證一次http請求過程中,一直都使用一個session呢,即一個Connection呢.而且還要保證http請求結束後正確的關閉.
好,現在我們知道了我們要解決的問題
1.如何保證http請求結束後正確的關閉session
2.如何保證http請求過程中一直使用同一個session
第一個問題很容易想到,使用過濾器
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) {
try {
filterChain.doFilter(request, response);
} catch (IOException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
} finally {
try {
HibernateUtil.commitTransaction();
} catch (Exception e) {
HibernateUtil.rollbackTransaction();
} finally {
HibernateUtil.closeSession();
}
}
}
要解決第二個問題我們必須先搞清楚,http請求在java中是以什麼樣的機制實現的,在java中一個請求就是一個線程,像流行的web容器Tomcat等,往往都是採用線程池機制的也就是說有n個線程在池子裏面,每當有http請求時,隨機從線程池中取出一個線程對象去處理請求,實際上多次請求可能使用的是同一線程也可能不是,這是隨機的.要保證整個請求中使用同一session最容易想到的就是把這個session綁定到線程上,在java中使用ThreadLocal可以輕鬆綁定變量,每個線程有一個自己的ThreadLocal,這個ThreadLocal會隨線程的銷燬一起銷燬,既然是每個線程有一個那麼多個線程間自然是不會有影響了,所以把session綁定在ThreadLocal裏面是最好的選擇了,
有關ThreadLocal的更多資料,大家可以百度或者參考
http://www-128.ibm.com/developerworks/cn/java/j-threads/index3.html
http://www.blogjava.net/jspark/archive/2006/08/01/61165.html
最後我把相關的代碼發出來
import java.sql.SQLException;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.sql.Connection;
import org.apache.log4j.Logger;
import java.io.File;
/**
*
* <p>
* Title:Hibernate工具類
* </p>
*
* <p>
* 利用ThreadLocal 綁定 Hibernate 的session
* </p>
*
* @author 孫鈺佳
* @mail [email protected]
* @version 1.0
*/
public class HibernateUtil {
/**
* Loger4j的logger
*/
private static final Logger logger = Logger.getLogger(HibernateUtil.class);
/**
* 存儲hibernate session的ThreadLocal
*/
private static final ThreadLocal sessionThread = new ThreadLocal();
/**
* 存儲事務的ThreadLocal
*/
private static final ThreadLocal transactionThread = new ThreadLocal();
/**
* Hibernate 的 Session工廠
*/
private static SessionFactory sessionFactory = null;
/**
* 初始化SessionFactory
*
* @param file
* Hibernate配置文件
*/
public static void initSessionFactory(File file) {
Configuration config = new Configuration();
config.configure(file);
sessionFactory = config.buildSessionFactory();
}
/**
* 獲取當前線程綁定的session
*
* @return Session
* @throws HibernateException
*/
public static Session getSession() {
Session s = (Session) sessionThread.get();
if (s == null) {
s = sessionFactory.openSession();
sessionThread.set(s);
} else {
Connection conn = s.connection();
try {
if (conn == null || conn.isClosed()) {
try {
s.close();
} catch (HibernateException e) {
logger.warn("close session error:" + e.getMessage(), e);
}
s = sessionFactory.openSession();
sessionThread.set(s);
}
} catch (SQLException e) {
throw new HibernateException(e);
}
}
return s;
}
/**
* 取得當前session的事務
*
* @return Transaction
*/
public static Transaction transaction() {
Transaction transaction = (Transaction) transactionThread.get();
if (transaction == null) {
transaction = getSession().beginTransaction();
transactionThread.set(transaction);
}
return transaction;
}
/**
* 提交當前session的事務
*/
public static void commitTransaction() {
Transaction transaction = (Transaction) transactionThread.get();
transactionThread.set(null);
if (transaction != null)
transaction.commit();
}
/**
* 回滾當前session的事務
*/
public static void rollbackTransaction() {
Transaction tx = (Transaction) transactionThread.get();
transactionThread.set(null);
if (tx != null)
tx.rollback();
}
/**
* 關閉當前線程使用的session
*/
public static void closeSession() {
Session session = (Session) sessionThread.get();
if (session != null) {
session.clear();
session.close();
sessionThread.set(null);
}
}
}
下面是一個調用的例子:
public static void main(String[] args) throws Exception {
HibernateUtil.initSessionFactory(new File(Test.class.getClassLoader()
.getResource("hibernate.cfg.xml").getFile()));
Session session = HibernateUtil.getSession();
HibernateUtil.transaction();
User u = new User();
u.setName("test");
session.save(u);
HibernateUtil.commitTransaction();
HibernateUtil.closeSession();
}