自定義類 Hibernate 框架的基本過程
1. 前言
不要一味地迷信,要善于質(zhì)疑,善于打破。這應該是我們在學習框架時應該保持的心態(tài)。
Hibernate 是所有 JDBC 框架中封裝最高的、使用起來最便利的框架之一。對于初學者而言,要么在感嘆它的神奇,要么敬畏它的存在。
但是,作為一個真正的開發(fā)者,應該有破有立的想法。
本節(jié)課程試圖通過一個簡易的 JDBC 框架的實現(xiàn)流程描述,讓大家更清晰地了解 Hibernate 框架的設計過程。
TIps: 框架的編寫是一個比較復雜的過程,并不是一言兩語能說清楚的,本文中的代碼介于偽代碼和真實代碼之間 ,僅僅用來幫助大家理解 Hibernate 的框架基礎原理。
2. 讀取配置信息
簡易框架主要模擬 3 個組件,分別對應 Hibernate 中的 Configuration、SessionFactory、Session 組件。
Configuration 組件的作用:解析配置文件中的信息。
Tips: 簡易框架中,保持配置文件格式及內(nèi)容和 Hiberante 的主配置文件一致。
查看一下 Configuration 類的結(jié)構(gòu):
public class Configuration {
//保存配置文件中的 property 元素信息
private Map<String,String> pros;
//保存配置文件中的映射類
private List<String> mappings;
public Configuration() {
this.pros=new HashMap<String, String>();
this.mappings=new ArrayList<String>();
}
/**
* 讀取配置文件
*/
public void configure() throws DocumentException {
configure("/hibernate.cfg.xml");
}
private void configure(String resource) throws DocumentException {
InputStream stream =Configuration.class.getResourceAsStream(resource);
doConfigure( stream);
}
/**
* 使用 DOM4j XML 解析器解決配置文件中的信息
*/
private void doConfigure(InputStream stream) throws DocumentException {
SAXReader saxReader=new SAXReader();
Document doc= saxReader.read(stream);
Element rootEle= doc.getRootElement();
Element sfEle= rootEle.addElement("session-factory");
List<Element> propertys= sfEle.elements("property");
for (Element ele : propertys) {
this.pros.put(ele.attributeValue("name"), ele.getText());
}
List<Element> mappings= sfEle.elements("mapping");
for (Element m : mappings) {
this.mappings.add(m.attributeValue("class"));
}
}
/**
*創(chuàng)建會話工廠
*
*/
public SessionFactory buildSessionFactory() {
return new SessionFactory(this.pros,this.mappings);
}
}
Hibernate 的主配置文件中有很多配置項,因受限于文章篇幅和本文初設目標,這里只解析 2 類信息:
- 數(shù)據(jù)庫連接信息;
- 實體類路徑信息。
基礎好的學習者可以查閱 Hibernate 的源代碼。
3. 會話工廠
會話工廠的核心作用:建立和數(shù)據(jù)庫系統(tǒng)的連接,創(chuàng)建會話對象。
創(chuàng)建時需要依賴 Configuration 組件解析出來的信息。Hiberante 的會話工廠的源代碼實現(xiàn)比較復雜,代碼具有良好的結(jié)構(gòu)性,會衍生出很多輔助性組件。
因為只是說明問題,本文中的工廠僅僅用來創(chuàng)建會話對象,不考慮性能、緩存、事務等各方面問題,也不刻意講究代碼的結(jié)構(gòu)性。
簡易框架中的會話工廠的代碼簡單但具有說明力:
public class SessionFactory {
private Map<String, String> pros;
private List<String> mappings;
private ThreadLocal<Session> threadLocal;
public SessionFactory(Map<String, String> pros, List<String> mappings) {
this.pros = pros;
this.mappings = mappings;
this.threadLocal = new ThreadLocal<Session>();
}
public Connection getConn() {
String driverClass = this.pros.get("connection.driver_class");
String url = this.pros.get("connection.url");
String userName = this.pros.get("connection.username");
String password = this.pros.get("connection.password");
try {
Class.forName(driverClass);
return DriverManager.getConnection(url, userName, password);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return null;
}
public Session openSession() {
return new Session(this.getConn(), this.mappings);
}
public Session getCurrentSession() {
Session session = this.threadLocal.get();
if (session == null) {
session = this.openSession();
this.threadLocal.set(session);
}
return session;
}
}
會話工廠類中有 2 個創(chuàng)建 Session 的方法,一個是普通的創(chuàng)建方法,另一個是從線程上下文中檢查是否已經(jīng)存在 Session 對象。
4. 會話對象
Session 對象提供具體的方法用來對數(shù)據(jù)庫中的數(shù)據(jù)進行相關操作。
public class Session<T> {
private Connection conn;
private List<String> mappings;
public Session(Connection conn, List<String> mappings) {
this.conn=conn;
this.mappings=mappings;
}
public T get(Class<T> clz,Serializable ser) {
//構(gòu)建 SQL
//得到結(jié)果集
//封裝數(shù)據(jù)
return null;
}
public Object load(Class<T> clz,Serializable ser) {
return null;
}
public int save() {
return 0;
}
//其它方法
}
在前面的《Hibernate 自動生成 SQL 語句》和《 Hibernate 自動進行數(shù)據(jù)封裝 》章節(jié)中已經(jīng)講解了如何通過反射構(gòu)建 SQL 和數(shù)據(jù)封裝。請查閱相關內(nèi)容。
因為延遲加載是 Hibernate 中一個比較重要的特性,所以,這里講解一下 load 方法的實現(xiàn)原理。load()方法調(diào)用時并沒有直接訪問數(shù)據(jù)庫,而是返回給用戶一個代理對象,只有當使用者調(diào)用代理對象的 getXXX()方法時,才會進入數(shù)據(jù)庫。
做到這一點并不難,使用代理設計模式便可。
代理設計模式中創(chuàng)建代理對象的方案有 2 種:
- 靜態(tài)代理對象;
- 動態(tài)代理對象。
一般都會使用動態(tài)方式創(chuàng)建代理對象,動態(tài)代理對象的常用創(chuàng)建方案有 2 種:
- 使用 JDK 中提供的 Proxy 類創(chuàng)建;
- 第三方實現(xiàn),如 cglib 庫。
使用 Proxy 創(chuàng)建時有一個前提條件,需要被代理的對象提供接口規(guī)范,使用 cglib 就不需要,因是第三方實現(xiàn),則需要在項目中導入依賴包。
通過代理設計模式的回調(diào)機制,load()方法就能實現(xiàn)當需要時再觸發(fā)對應的數(shù)據(jù)庫操作。
5. 小結(jié)
本節(jié)課以簡易代碼的方式描述了 Configuration、SessionFactory、Session 3 個組件的功能實現(xiàn),以及其相互之間的依賴關系。
但并沒有更深入的接軌 Hibennate 的源代碼,Hiberanate 源代碼中有很多商業(yè)性的解決方案,值得大家深究參考。
希望通過本次課程,能讓大家更進一步的認識 Hibernate 。