Hibernate 模板設(shè)計模式
1. 前言
本節(jié)課和大家一起使用模板設(shè)計模式重構(gòu) Hibernate 操作流程,通過本節(jié)課程內(nèi)容,你將了解到:
- 如何運用模板設(shè)計模式重構(gòu) Hibernate 操作流程;
- 持久化對象與序列化接口;
2. 模板設(shè)計模式
學(xué)習(xí) Hibernate 的過程,如同訓(xùn)練場上練兵。終有一天,你要奔赴真正的戰(zhàn)場(真實項目)。
所以,隨時隨地,要審視代碼、重構(gòu)代碼。
2.1 什么是模板化操作
OOP 中有一個編碼原則 : 寫僅寫一次 。翻譯過來就是,不要重復(fù),要重用。
如何簡化測試實例:
答案是:使用模板設(shè)計模式進(jìn)一步封裝 Hibernate 的操作。
在真實項目中,Hibernate 僅僅只是完成項目中的一部分工作,需要和其它,如 Spring 等框架聯(lián)合工作,一起承擔(dān)整體項目的開發(fā)。
Spring 框架中就提供的有 Hibernate 模板對象。
模板設(shè)計模式的應(yīng)用場景:
一個常規(guī)的、頻繁的操作代碼中,大部分代碼不需要變動,只有小部分代碼需要根據(jù)需求變動。這種場景下的代碼,可認(rèn)為是模板化操作流程代碼,可使用模板設(shè)計模式進(jìn)行重構(gòu)。
JDBC 就是一個標(biāo)準(zhǔn)化的模板化操作過程??梢哉f,Hibernate 是一個操作 JDBC 的模板化框架。
Hiberante 雖然高度簡化 JDBC 操作流程,但使用期間,還是需要按部就班的:
- 創(chuàng)建配置對象;
- 創(chuàng)建會話工廠;
- 創(chuàng)建會話對象;
- 創(chuàng)建事務(wù)對象;
- 終于輪到完成需求;
- 嘿,記得…… 關(guān)閉會話對象!謝謝提醒,差點忘記了。
好無聊呀,重復(fù)的事情,總是讓人很容易麻木。內(nèi)心掙扎時刻,便是想辦法應(yīng)對時刻。
本文中把不需要改變的代碼稱為模板代碼。
好了,現(xiàn)在開始,一起使用模板設(shè)計模式繼續(xù)簡化 Hibernate 的操作流程。這種感覺就像風(fēng)一樣自由。
2.2 模板化流程
先畫一個圖,簡要說一下模板化的基本思路:
一個模板對象中有 2 種類型代碼:
- 模板代碼: 公共的代碼,不需要變更的代碼;
- 通知代碼: 告訴調(diào)用者,此處應(yīng)該是你來做了。一般采用接口的方式進(jìn)行通知。
可以得出一個結(jié)論,對于一件事情,原來完全是靠調(diào)用者獨立完成,現(xiàn)在分?jǐn)偟搅藘蓚€對象上,模板對象完成公共部分代碼,調(diào)用者完成屬于自己需求的代碼。
有了上面的理解基礎(chǔ),便知,一個完整的模板調(diào)用過程,會涉及到 3 個角色:
- 調(diào)用者角色: 使用者;
- 模板角色: 封裝公共代碼,簡化使用者的操作;
- 接口角色: 調(diào)用者和模板角色之間的連接通道,互相通信;
下面進(jìn)行具體實例編寫:
- 構(gòu)建一個 HibernateTemplate 類,模板角色:
public class HibernateTemplate<T extends Serializable > {
private SessionFactory sessionFactory;
public HibernateTemplate() {
// 模板代碼
Configuration configuration = new Configuration().configure();
// 模板代碼
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties())
.buildServiceRegistry();
// 模板代碼
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
}
public T template(Notify<T> notify) {
// 模板代碼
Session session = sessionFactory.openSession();
// 模板代碼
Transaction transaction = null;
try {
// 模板代碼
transaction = session.beginTransaction();
// 通知代碼
T result = notify.action(session);
transaction.commit();
return result;
} catch (Exception e) {
transaction.rollback();
} finally {
session.close();
}
return null;
}
}
- 接口角色:
public interface Notify<T extends Serializable> {
T action(Session session);
}
- 調(diào)用者角色:此處為測試實例:
HibernateTemplate<Student> hibernateTemplate=new HibernateTemplate<Student>();
Student stu= hibernateTemplate.template(new Notify<Student>() {
@Override
public Student action(Session session) {
return (Student)session.get(Student.class, new Integer(1));
}
});
System.out.println(stu.getStuName());
- 控制臺輸出結(jié)果不再貼出。不要懷疑,結(jié)果一定是你所期望的。
模板對象中的接口非常重要:
- 通知作用,并等待調(diào)用者的響應(yīng)。接口中的方法也可稱為回調(diào)方法;
- 模板對象通過接口的方式暴露出 Session 給調(diào)用者,調(diào)用者不需要關(guān)心 Session 是怎么來的,安心使用便是。
模板對象中可以進(jìn)一步封裝 Session 對象中的相關(guān)方法,如:
public T get(Class<?> clz, Serializable ser) {
// 模板代碼
Session session = sessionFactory.openSession();
// 模板代碼
Transaction transaction = null;
try {
// 模板代碼
transaction = session.beginTransaction();
T result = (T) session.get(clz, ser);
transaction.commit();
return result;
} catch (Exception e) {
transaction.rollback();
} finally {
session.close();
}
return null;
}
測試實例:
HibernateTemplate<Student> hibernateTemplate=new HibernateTemplate<Student>();
Student stu= hibernateTemplate.get(Student.class, new Integer(1));
System.out.println(stu.getStuName());
是不是開心的不要不要的,除了在模板類中需要寫一次 Hibernate 常規(guī)流程外,具體操作時,直接上模板對象。
對于任何知識不要理所當(dāng)然地接受,要善于發(fā)現(xiàn)代碼中冗余的地方,逐步形成代碼意識,隨時改進(jìn)代碼。
3. 序列化
不知道大家發(fā)現(xiàn)沒有,模板對象的泛型聲明:
public class HibernateTemplate<T extends Serializable>{}
前面課程使用 Session 對象中的方法時,Serializable 接口時不時的就蹦出來,為什么 Hibernate 要求持久化類實現(xiàn) Serializable 接口?
為什么使用 Sesssion 的方法,某些參數(shù)也需要一個實現(xiàn) Serializable 接口的對象。
public Object load(Class theClass, Serializable id);
public Object get(Class clazz, Serializable id);
原因很簡單,如果你真正了解什么是序列化。
所謂序列化,通俗理解:
以二進(jìn)制的形式存儲對象中的數(shù)據(jù),這個過程就叫序列化。相反的,把存儲的二進(jìn)制數(shù)據(jù)恢復(fù)成對象數(shù)據(jù),這個過程是反序列化。
序列化的目的,就是要以對象為單元進(jìn)行數(shù)據(jù)存儲,存儲并不限于本地磁盤,可以是網(wǎng)絡(luò)等環(huán)境。
序列化屏蔽底層繁瑣的編碼、解碼過程,完全以一種面向?qū)ο蟮睦砟钸M(jìn)行數(shù)據(jù)存儲。提高開發(fā)效率。
Hiberante 為什么要求持久化對象實現(xiàn)序列化?
- 緩存數(shù)據(jù): 如先把一個查詢出來的對象數(shù)據(jù)以序列化的方式存儲到內(nèi)存或磁盤中,需要時再讀出來,再持久化到數(shù)據(jù)庫中;
- 網(wǎng)絡(luò)數(shù)據(jù)傳輸: 需要把持久化數(shù)據(jù)從一個系統(tǒng)傳到另一個系統(tǒng)時,可能兩個系統(tǒng)是基于兩個平臺,在異構(gòu)化的系統(tǒng)中通過二進(jìn)制進(jìn)行數(shù)據(jù)傳遞,可打破這種壁壘。
不管怎樣,讓對象具有序列化能力,有點像《終結(jié)者》中的液態(tài)機(jī)器人,隨時把自己液態(tài)化,來去自由。適應(yīng)不同的需求場景。
4. 小結(jié)
本節(jié)課也要到說再見的時候,留一個問題給大家:如果在持久化類中重寫 equals 方法,為什么也要求重寫 hashCode 方法。
答案其實就在于你對這兩個方法的理解了。
好了!本節(jié)課,和大家一起使用模板設(shè)計模式封裝了 Hibernate 的操作代碼。讓 Hibernate 的使用過程變得更簡單,更是為了適應(yīng)真實項目需求。
本節(jié)課程也和大家一起聊到了持久化對象為什么要實現(xiàn)序列化接口。