自定義類 Hibernate 框架的基本過程
1. 前言
不要一味地迷信,要善于質(zhì)疑,善于打破。這應(yīng)該是我們?cè)趯W(xué)習(xí)框架時(shí)應(yīng)該保持的心態(tài)。
Hibernate 是所有 JDBC 框架中封裝最高的、使用起來最便利的框架之一。對(duì)于初學(xué)者而言,要么在感嘆它的神奇,要么敬畏它的存在。
但是,作為一個(gè)真正的開發(fā)者,應(yīng)該有破有立的想法。
本節(jié)課程試圖通過一個(gè)簡易的 JDBC 框架的實(shí)現(xiàn)流程描述,讓大家更清晰地了解 Hibernate 框架的設(shè)計(jì)過程。
TIps: 框架的編寫是一個(gè)比較復(fù)雜的過程,并不是一言兩語能說清楚的,本文中的代碼介于偽代碼和真實(shí)代碼之間 ,僅僅用來幫助大家理解 Hibernate 的框架基礎(chǔ)原理。
2. 讀取配置信息
簡易框架主要模擬 3 個(gè)組件,分別對(duì)應(yīng) 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)建會(huì)話工廠
*
*/
public SessionFactory buildSessionFactory() {
return new SessionFactory(this.pros,this.mappings);
}
}
Hibernate 的主配置文件中有很多配置項(xiàng),因受限于文章篇幅和本文初設(shè)目標(biāo),這里只解析 2 類信息:
- 數(shù)據(jù)庫連接信息;
- 實(shí)體類路徑信息。
基礎(chǔ)好的學(xué)習(xí)者可以查閱 Hibernate 的源代碼。
3. 會(huì)話工廠
會(huì)話工廠的核心作用:建立和數(shù)據(jù)庫系統(tǒng)的連接,創(chuàng)建會(huì)話對(duì)象。
創(chuàng)建時(shí)需要依賴 Configuration 組件解析出來的信息。Hiberante 的會(huì)話工廠的源代碼實(shí)現(xiàn)比較復(fù)雜,代碼具有良好的結(jié)構(gòu)性,會(huì)衍生出很多輔助性組件。
因?yàn)橹皇钦f明問題,本文中的工廠僅僅用來創(chuàng)建會(huì)話對(duì)象,不考慮性能、緩存、事務(wù)等各方面問題,也不刻意講究代碼的結(jié)構(gòu)性。
簡易框架中的會(huì)話工廠的代碼簡單但具有說明力:
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;
}
}
會(huì)話工廠類中有 2 個(gè)創(chuàng)建 Session 的方法,一個(gè)是普通的創(chuàng)建方法,另一個(gè)是從線程上下文中檢查是否已經(jīng)存在 Session 對(duì)象。
4. 會(huì)話對(duì)象
Session 對(duì)象提供具體的方法用來對(duì)數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行相關(guān)操作。
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 自動(dòng)生成 SQL 語句》和《 Hibernate 自動(dòng)進(jìn)行數(shù)據(jù)封裝 》章節(jié)中已經(jīng)講解了如何通過反射構(gòu)建 SQL 和數(shù)據(jù)封裝。請(qǐng)查閱相關(guān)內(nèi)容。
因?yàn)檠舆t加載是 Hibernate 中一個(gè)比較重要的特性,所以,這里講解一下 load 方法的實(shí)現(xiàn)原理。load()方法調(diào)用時(shí)并沒有直接訪問數(shù)據(jù)庫,而是返回給用戶一個(gè)代理對(duì)象,只有當(dāng)使用者調(diào)用代理對(duì)象的 getXXX()方法時(shí),才會(huì)進(jìn)入數(shù)據(jù)庫。
做到這一點(diǎn)并不難,使用代理設(shè)計(jì)模式便可。
代理設(shè)計(jì)模式中創(chuàng)建代理對(duì)象的方案有 2 種:
- 靜態(tài)代理對(duì)象;
- 動(dòng)態(tài)代理對(duì)象。
一般都會(huì)使用動(dòng)態(tài)方式創(chuàng)建代理對(duì)象,動(dòng)態(tài)代理對(duì)象的常用創(chuàng)建方案有 2 種:
- 使用 JDK 中提供的 Proxy 類創(chuàng)建;
- 第三方實(shí)現(xiàn),如 cglib 庫。
使用 Proxy 創(chuàng)建時(shí)有一個(gè)前提條件,需要被代理的對(duì)象提供接口規(guī)范,使用 cglib 就不需要,因是第三方實(shí)現(xiàn),則需要在項(xiàng)目中導(dǎo)入依賴包。
通過代理設(shè)計(jì)模式的回調(diào)機(jī)制,load()方法就能實(shí)現(xiàn)當(dāng)需要時(shí)再觸發(fā)對(duì)應(yīng)的數(shù)據(jù)庫操作。
5. 小結(jié)
本節(jié)課以簡易代碼的方式描述了 Configuration、SessionFactory、Session 3 個(gè)組件的功能實(shí)現(xiàn),以及其相互之間的依賴關(guān)系。
但并沒有更深入的接軌 Hibennate 的源代碼,Hiberanate 源代碼中有很多商業(yè)性的解決方案,值得大家深究參考。
希望通過本次課程,能讓大家更進(jìn)一步的認(rèn)識(shí) Hibernate 。