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