這一章包括:
n?????
使用
DAO
模式創建抽象層。
n?????
使用層的父型模式簡化
resource cleanup
(資源清除)代碼
n?????
用
spring
組織你的項目
?
理解
Hibernate
的底層將需要很長一段時間才能在你的項目上使用。但是在
Hibernate
庫的基礎之外,如
SessionFactory
(會話工廠),
Session
(會話),
mapping files
(
映射文件)和
hibernate Query Language(HQL)
(
Hibernate
查詢語言),在一定高度組織一個應用程序并不是總是那么清楚的。 你能在你的項目中應用一些模式和好的經驗。一些好的經驗來自于社區;而另外一些來自應用于持久化的Java企業模式。這一章的目的就是帶給你這些。
???
寫程序有點像用小朋友的
alphabet blocks
(譯者注:一種表面有字母的積木)建塔。如果你是建個小塔,那么你不需要仔細關心是怎么堆的。但如果你建一個大的塔,那么你可能要站得更高,你需要稍微不同的技術設置。多一點計劃,多一點怎樣堆的技術和可能使用一些超強的粘合劑。
???
無論是建立玩具塔還是寫軟件都要使用好的工具和技術。因此這一章就是怎樣建立一座高塔。我們將討論一些普通的模式:
Data Access Object(DAO)
(
封裝底層數據操作
)和層的父型模式。另外,
spring
,
流行的開源項目將為簡化你的代碼提供組織的工具。總之,這一章將給這個工具一個總的看法和怎樣簡化你的
Hibernate
項目。
?
章節目標:
在這一章,你將完成一些幾點:
n????????
創建抽象層,使用
DAO
模式集中
SQL
語句,因此簡化客戶端對象使用。
n????????
使用層的父型模式改善
DAO
對象,簡化
resource
cleanup
(資源清除)代碼。
n????????
使用
spring
組織和簡化你的
DAO
代碼。
?
前提條件:
假設你已經完成以下幾點:
n????????
熟悉模式的概念。
n????????
理解
session
(會話)和
transaction
(事務)的工作原理,特別是怎樣使用持久化被打開
session
(會話)鏈接的對象。
n????????
你正在尋找一種能組織大型項目的技術。也就是說,使用好的框架代碼將利于項目的以后的擴展。
?
7.1
無處不在的
DAO
模式
大多數
Java/J2EE
開發者都或多或少熟悉
DAO
模式。它是
Sun
公司特地在
Java
藍圖中強調的核心模式之一,在許多
Java
書籍中都提到過。它是在使用持久化數據存儲的應用程序中的采取的第一個首要模式。一般使用在使用
SQL
的應用程序中,同樣也適用于
Hibernate
的應用程序。
?
7.1.1
集中
HQL
DAO
模式的目的能在在一個簡單問題的答案中找到:你的數據操作代碼放在哪里?如果你面對一個遺留程序而苦惱,因為這個程序中
SQL
代碼就像鳥槍發射那樣四處都是,而你又沒有這方面的經驗。其實這并沒什么。需要從新命名表中的列嗎?準備好手中的槍,在整個程序中修改,以確保你沒有遺漏任何
SQL
語句。
DAO
模式鼓勵開發者集中
SQL
語句。那么怎樣才是好的
SQL
和
HQL
呢?在應用程序中將
HQL
放在一個地方以便以后更容易維護和修改。新手可能不能很好的確定將
HQL
放在哪?他們將
HQL
放在DAO中。
Figure 7.1
顯示了
Event
和
EventDao
怎樣和數據庫互動。
Figure 7.1
Event
和
EventDao
與數據庫互動的圖
?
通過前面章節知道,一個健壯的對象查詢語句需要有錯誤處理管理,事務和
resource cleanup
(資源清除)。在余下的程序中最好隱藏以上那些。例如接下來的
Hibernate
程序,使程序更容易改變
ORM
(
object/relational mapping
)
(
對象關系映射
)實現(例如:改變成
JDO
)1。更重要的是,這個策略簡化了客戶端怎樣和持久化層互動;他們不需要知道
session
(會話),
transaction
(事務)的邊界,或者在他們使用之后是否被清除。
?
DAO
也有類型
???
你能使用
DAO
兩種基本類型中的一種:
n????????
應用級
DAO(DAO per application)
:在一個應用中有一個中心的
DAO
對所有的實體對象添、刪、改、查。
n????????
類級
DAO(DAO per class)
:每個實體類都有自己的
DAO
,用于自身實例的添、刪、改、查。
Event
對象由
EventDao
負責。
你也可以應用其他鏡像變量,例如使用模塊級
DAO(DAO per module)
,但是最終的選擇還是依賴于你有少個持久化對象。在有許多類的情況下,我們偏愛使用類級DAO策略。應用級
DAO
策略會出現“
bloatware
”類。第二,與類級
DAO
命名對稱能更好的記憶。如果你需要找到
Event
類,你就能夠記得起它的
DAO
是
EventDao
。最后,它滿足開-關原則,原則規定類將因為擴充而打開,因為修改而關閉。你能夠添加一個新的持久化類,而不需要修改中心的
DAO
。因此,讓我們在示例中使用類級
DAO
。
?
簡單的
DAO
???
Listing 7.1
顯示了一個簡單的
DAO
,它能處理滿足大多數實體需要的基本的CRUD(添、刪、改、查)業務。除了這些功能,它還有一些功能,因此客戶端對象不用擔心。
n????????
包括業務級
session
(
session per operation
)
;每個
session
(會話)都有添、刪、改、查方法。
n????????
提供一個業務級
transaction
(事務)
(transaction
per operation)
。客戶端對象不用擔心開始和提交事務。
n????????
在
Hibernate2.1
中,處理捕獲和
Hibernate
拋出的預期異常,將它們變為非預期異常,將不會使客戶端代碼混亂。如果你使用
Hibernate3.x
,你能夠讓沒有從新拋出的非預期異常通過。(
In Hibernate 2.x, handles catching and handling
the checked exceptions that Hibernate throws, turning them into unchecked
exceptions, which won’t clutter up client code. If you use Hibernate 3.x, you
can just let the unchecked
HibernateException
s go without rethrowing
.
)
n????????
輸入特性鮮明和明確的
DAO
;
EventDat
僅為
Event
工作,意思就是客戶端代碼不必執行手動的操作。(
Features strongly typed and
explicit DAOs; the
EventDao
only
works with
Event
s, meaning client code
doesn’t have to perform manual casting.
)
?
Listing 7.1
一個簡單的有
添、刪、改、查方法的
EventDao
---------------------------------------------------------------------------------------------
package
?com.manning.hq.ch07;
?
import
?org.hibernate.HibernateException;
import
?org.hibernate.Session;
import
?org.hibernate.Transaction;
import
?org.apache.commons.logging.Log;
import
?org.apache.commons.logging.LogFactory;
import
?com.manning.hq.ch07.Event;
import
?com.manning.hq.ch07.HibernateFactory;
import
?java.util.List;
/**
*?DAO管理持久Evnet
*/
public
?
class
?SimpleEventDao?{
??? Log?log?
=
?LogFactory.getLog(SimpleEventDao.
class
);
??? private
?Session?session;
??? private
?Transaction?tx;
??? public
?SimpleEventDao()?{
??? ??? HibernateFactory.buildIfNeeded();
//
初始化SessionFactory。
??? }
?
??? public
?
void
?create(Event?event)?
throws
?DataAccessLayerException?{
??? ??? try
?{
??? ??? ??? startOperation();
//
打開Session和開始transaction
??? ??? ??? session.save(event);
//
保存Event。
??? ??? ??? tx.commit();
??? ??? }?
catch
?(HibernateException?e)?{
??? ??? ??? handleException(e);Rolls?back和拋出異常。
??? ??? }?
finally
?{
??? ??? ??? HibernateFactory.close(session);
??? ??? }
??? }
??? public
?
void
?delete(Event?event)?
throws
?DataAccessLayerException?{
??? ??? try
?{
??? ??? ??? startOperation();
??? ??? ??? session.delete(event);
??? ??? ??? tx.commit();
??? ??? }?
catch
?(HibernateException?e)?{
??? ??? ??? handleException(e);
??? ??? }?
finally
?{
??? ??? ??? HibernateFactory.close(session);
??? ??? }
??? }
??? public
?Event?find(Long?id)?
throws
?DataAccessLayerException{
??? ??? Event?event?
=
?
null
;
??? ??? try
?{
??? ??? ??? startOperation();
??? ??? ??? event?
=
?(Event)?session.load(Event.
class
,?id);
??? ??? ??? tx.commit();
??? ??? }?
catch
?(HibernateException?e)?{
??? ??? ??? handleException(e);
??? ??? }?
finally
?{
??? ??? ??? HibernateFactory.close(session);
??? ??? }
??? ??? return
?event;
??? }
??? public
?
void
?update(Event?event)?
throws
?DataAccessLayerException?{
??? ??? try
?{
??? ??? ??? startOperation();
??? ??? ??? session.update(event);
??? ??? ??? tx.commit();
??? ??? }?
catch
?(HibernateException?e)?{
??? ??? ??? handleException(e);
??? ??? }?
finally
?{
??? ??? ??? HibernateFactory.close(session);
??? ??? }
??? }
??? private
?
void
?handleException(HibernateException?e)?
throws
?DataAccessLayerException?{
??? ??? HibernateFactory.rollback(tx);
??? ??? throw
?
new
?DataAccessLayerException(e);
??? ??? //
?二選一,你能夠從新拋出,就像…
??? ??? //
?throw?e;
??? }
??? private
?
void
?startOperation()?
throws
?HibernateException?{
??? ??? session?
=
?HibernateFactory.openSession();
??? ??? tx?
=
?session.beginTransaction();
???? }
}
package
?com.manning.hq.ch07;
public
?
class
?DataAccessLayerException?
extends
?RuntimeException?{
//
其他的構造函數省略。
??? public
?DataAccessLayerException(Throwable?cause)?{
??? ??? super
(cause);
??? }
}
---------------------------------------------------------------------------------------------
???
SimpleEventDao
是個極其簡單的
DAO
,它有添,刪,改,查四種方法。每種方法在一個
transaction
(事務),打開和關閉一個
session
(會話)范圍內處理業務。它是明確的,也就是說每個
Event
都有獨有的業務處理,因此客戶端類不需要再處理這些了。當這個實現是簡單的(以后可能隨著擴展會變大),客戶端代碼在運行
event
時代碼就會變得很短。(
While this implementation is simple (perhaps overly so, as we will explore
here later), it greatly shortens the client code that works with events.
)因此創建和查詢一個event就象下面的一樣簡單:---------------------------------------------------------------------------------------------
Event?event?
=
?
new
?Event();
event.setName(
"
A?new?Event
"
);
EventDao?eventDao?
=
?
new
?EventDao();
eventDao.create(event);
Event?foundEvent?
=
?eventDao.find(event.getId());
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
正如你看到的,不需要處理凌亂的異常,這里也不需要處理
resource cleanup
(資源清除)。現在讓我們討論更多一些這個實現出現的問題,并且我們該怎樣提高它。
?
7.2
分析
DAO
???
我們在前面章節練習的簡單的DAO實現出現了一些問題,有些你或許已經遇到。讓我們看一看。
?
7.2.1
boilerplate
code
(
樣板代碼)
Listing 7.1
包括許多資源管理和異常處理代碼。每個方法都有打開
session
(會話),開始事務,執行自己的商務業務,提交事務,事務回滾,最后關閉
session
(會話)。每個方法基本就像下面那樣:
-------------------------------------------------------------------------------
?
try
?{
???? startOperation();
???? session.save(event);
???? tx.commit();
?}?catch?(HibernateException?e)?{
???? handleException(e);
?}?finally?{
???? HibernateFactory.close(session);
?}
-------------------------------------------------------------------------------
Session.save(event)
這一行是每個方法中唯一改變的。甚至
refactoring
幾個便利的方法(譯者注:找了不少詞典,都沒有找到“
refactor
”),如
startOperation
()
和
handleException
()
,都不能完全擺脫你的樣板代碼。(
Even
refactoring out a few convenience methods,such as
startOperation()
and
handleException()
, doesn’t completely rid you
of boilerplate code
)一個
potential solution
(潛在的解決方案)就是在7.3節討論的層的父型模式。
?
7.2.2
P
otential
duplication
(
潛在的復制)
???
增加一個新的
DAO
變得很簡單,只需要復制和粘貼。如果你需要其他的
DAO
,如
LocationDao
,
我們將復制和粘貼
EventDao
,然后在每個方法中改變關聯的少許幾行代碼。因為我們知道復制是所有程序的惡魔之首,顯然需要做點事情。建立層的父型模式是非常有幫助的。
?
7.2.3
游離對象
???
因為每個方法都是與單一的
session
(會話)和
transaction
(事務)運作的,與
DAO
運作的所有的
Event
是嚴格的游離對象。這個行為也許是美好的,但它不能夠利用
Hibernate
的自動臟對象檢查和
session-level
object cache
(會話級對象緩存)。例如,假如客戶端這樣寫:
---------------------------------------------------------------------------------------------
Event?foundEvent?
=
?eventDao.find(event.getId());
foundEvent.setDuration(
30
);
eventDao.update(foundEvent);
---------------------------------------------------------------------------------------------
Find
運行在一個
session
(會話)中,
update
運行在另一個
session
(會話)中。它一定會運行,但如果
find
和
update
能以某種方法共享一個
session
(會話)那才是真正的好。并且,它能更好的避免在
session
(會話)周圍的凌亂的方法署名。當它運作時,它是難看的,因此我們不想看到下面這樣:
---------------------------------------------------------------------------------------------
Session?session?
=
?HibernateFactory.openSession();
Event?foundEvent?
=
?eventDao.find(event.getId(),?session);
foundEvent.setDuration(
30
);
eventDao.update(foundEvent,?session);?
---------------------------------------------------------------------------------------------
???
給方法增加
session
(會話)參數,強制責任,管理和在客戶端代碼共享
session
(會話)。這樣會帶來重復,復雜和潛在的錯誤。
???
這個問題的一個潛在的解決方案是眾所周知的
Thread Local Session
模式。這個模式將在第八章介紹,因此我們不會在這里直接介紹。我們改用另外一種框架練習,
spring
,它使用
Thread
Local Session
模式,在下面會有介紹。
?
7.3
層的父型模式
???
常規的
J2EE
智者認為應用程序被劃分成不同的層。假設你的應用程序已經有這些層了,當然,依賴于你所讀的書。一些普遍的層的選擇如下:
n????????
表示層,這里是所有用戶交互和表示的代碼。
n????????
領域層,這里是邏輯的商務代碼。
n????????
持久層,這里是數據存儲操作的代碼。
???
不管你的應用程序是否有這些層,在層中的每個對象都有某些共同的代碼能夠被固定到一個類中,這一點是非常普通的。這個原因出現了層的父型模式,每一層都有“一種類型,這種類型作為所有的層中的類型的父型”2。你能夠使用父型模式簡化你的
DAO
。
???
Figure7.2
用一個簡單的層次顯示了層的父型
AbstractDao
,它提供了
protected
方法,在子類中可以覆蓋和改為
public
。
???
Figure 7.2
層的父型
AbstractDao
的圖
???
下一步就是創建
AbstractDao
,下一節將介紹。
?
7.3.1
創建
AbstractDao
???
第一步是創建你的父型,
AbstractDao
,所有的
DAO
最后將擴展。
Listing
7.2
顯示了類的內容。
Listing 7.2
層的父型實現,
AbstractDao
,它有所有
DAO
的共同的業務。
-----------------------------------------------------------------------------------------
??package?com.manning.hq.ch07;
import?org.hibernate.HibernateException;
import?org.hibernate.Query;import?org.hibernate.Session;
import?org.hibernate.Transaction;
import?java.util.List;
??
/**
*?層的父型處理所有DAO共同的運作
*/
public?abstract?class?AbstractDao{
??? private?Session?session;
??? private?Transaction?tx;
??? public?AbstractDao()?{
??? ??? HibernateFactory.buildIfNeeded();
??? }
??? protected?void?saveOrUpdate(Object?obj){//運作是普通的,而不是特指域對象
??? ??? try?{
??? ??? ??? startOperation();
??? ??? ??? session.saveOrUpdate(obj);
??? ??? ??? tx.commit();
??? ??? }?catch?(HibernateException?e)?{
??? ??? ??? handleException(e);
??? ??? }?finally?{
??? ??? ??? HibernateFactory.close(session);
??????? }
??? }
??? protected?void?delete(Object?obj)?{
??? ??? try?{
??? ??? ??? startOperation();
??? ??? ??? session.delete(obj);
??? ??? ??? tx.commit();
??? ??? }?catch?(HibernateException?e)?{
??? ??? ??? handleException(e);
??? ??? }?finally?{
??? ??? ??? HibernateFactory.close(session);
??? ??? }
??? }
??? protected?Object?find(Class?clazz,?Long?id){//查找基于類和id的持久化對象
??? ??? Object?obj?=?null;
??? ??? try?{
??? ??? ??? startOperation();
??? ??? ??? obj?=?session.load(clazz,?id);
??? ??? ??? tx.commit();
??? ??? }?catch?(HibernateException?e)?{
??? ??? ??? handleException(e);
??? ??? }?finally?{
??? ??? ??? HibernateFactory.close(session);
??? ??? }
??? ??? return?obj;
??? }
??? protected?List?findAll(Class?clazz)?{
??? ??? List?objects?=?null;
??? ??? try?{
??? ??? ??? startOperation();
??? ??? ??? Query?query?=?session.createQuery("from?"?+?clazz.getName());
??? ??? ??? objects?=?query.list();
??? ??? ??? tx.commit();
??? ??? }?catch?(HibernateException?e)?{
??? ??? ??? handleException(e);
??? ??? }?finally?{
??? ??? ??? HibernateFactory.close(session);
??? ??? }
??? ??? return?objects;
??? }
??? protected?void?handleException(HibernateException?e)??throws?DataAccessLayerException?{
??? ??? HibernateFactory.rollback(tx);
? ?? ? throw?new?DataAccessLayerException(e);
??? }
??? protected?void?startOperation()?throws?HibernateException?{
??? ??? session?=?HibernateFactory.openSession();
??? ??? tx?=?session.beginTransaction();
??? }
}
--------------------------------------------------------------------------------------------
????在這個Listing中,你看到了共同的CRUD方法,包括save,?find和delete方法都放入AbstractDao類中。他們是
generic和protected,因此子類能夠調用它們。這樣你的EventDao將簡化。這里是一個簡單的簡化了的方法:
---------------------------------------------------------------------------------------------
public?????class???ImprovedEventDao???extends???AbstractDao?{
//??其他的方法省略
??? public???void??create(Event?event)??throws??DataAccessLayerException?{
??? ??? saveOrUpdate(event);
??? }
??? public??Event?find(Long?id)??throws??DataAccessLayerException?{
??? ??? return??(Event)?find(Event.?class?,?id);
??? }
}
--------------------------------------------------------------------------------------------
??? ImprovedEventDao的僅有的責任就是執行業務和委托調用父類。樣板代碼和潛在的復制的雙重問題得到解決。當我們增加一個新的實體對象時,如:Locations或者Speakers,添加新的DAO--使用AbstractDao作為層的父型,將是非常迅速的。
--------------------------------------------------------------------------------------------
public?classImprovedLocationDao extendsAbstractDao?{
//其他的方法省略
??? publicvoid create(Location?location) throws??DataAccessLayerException?{
??? ??? saveOrUpdate(location);
??? }
??? publicLocation?find(Long?id)??throws??DataAccessLayerException?{
??? ??? return??(Location)?find(Location.?class?,?id);
??? }
}
---------------------------------------------------------------------------------------------
通過層的父型的介紹,剩余的問題得到解決,
DAO
與游離對象運作。我們想在我們的方法中共享會話,甚至通過不同的
DAO
。為了做到這一點,我們將學習一個新的流行框架,
Spring
.
?
7.4
Spring
框架
??????
我們已經注意到已經定義的
DAO
實現中的一些缺陷。復制資源管理代碼和使用業務級的
session
(
session per operation
)使解決方案會比我們需要的更加復雜,而且也沒有我們喜歡的靈活。我們一定能過編寫更好的更健壯的解決方案。幸運的是,我們不需要擔心
---
一個優秀的開源解決方案,
Spring
框架已經提供給我們了。
?????? Spring
解決了比幫助我們解決
Hibernate
更多的問題。這是事實,“一個輕量級的容器,允許開發者連接商務對象,
DAO
和資源就像
JDBC DataSources
和
Hibernate SessionFactories
。”
3??
它使用中心
XML
配置文件去管理資源,甚至它自身的
web MVC
框架(
Model-View-Controller
)。
Spring
是普通的框架,就是說它可以使用在許多不同的位置中。如果你不熟悉
Spring
,你也許會想我該怎樣使用它,你也許認為他僅僅是個框架,假設用來提供某種編譯。因此我們將在這里展示。
?????? Spring
已經被考慮成熟的分割為穩固的集中的若干模塊,包括我們先前提到的
MVC
web
框架,
JDBC
支持,
aspect-oriented programming
(
AOP
)(
面向剖面編程
)和
ORM
模塊。這樣就允許你使用其中你需要的,而不用學習或者考慮其他的。為了我們的目的,我們將僅僅學習怎樣簡化你的
Hibernate
代碼,也就是
ORM
模塊。最佳的開始的地方是模板。
??????
首先,你需要獲得
Spring
框架的副本,你可以在
www.springframework.org
找到。解壓到
applications
目錄下的
Hibernate
旁邊。
Spring
有許多可選擇的包,但為了簡單,你只需要考慮一個
JAE
包,它就是
applications\spring-framework-1.2-rc2\dist\spring.jar
文件。將它加入到
build.xml
文件的
classpath
中。
-------------------------------------------------------------------------------------------------------------------------
<
property?name
=
"
spring.version
"
?value
=
"
1.2-rc2
"
/>
<
property?name
=
"
spring.lib.dir
"
??? value
=
"
${applications.dir}/spring-framework-${spring.version}
"
/>
<
path?id
=
"
spring.lib.path
"
>
<
fileset?dir
=
"
${spring.lib.dir}\dist
"
>
<
include?name
=
"
**/spring.jar
"
/>
</
fileset
>
</
path
>
<
path?id
=
"
runtime.classpath
"
>
//
?其他的?paths?省略。
<
path?refid
=
"
spring.lib.path
"
/>
<
path
>
-------------------------------------------------------------------------------------------------------------------------
??????
這個代碼配置了
Spring
使它能過在我們的示例項目中使用。
Hibernate3
最近已經發布(在出版的時候),其他支持項目如
Spring
也跟上提供支持了。這里我們使用最新的版本。另外,因為
Spring
不得不支持
Hibernate 2
和
Hibernate 3,
一個新的包,
org.springframework.orm
。
hibernate3
已經將它加入到
Hibernate3
的項目中了。接下來,讓我們看看
Spring
怎樣被使用來簡化我們的示例項目。
?
7.4.1
在模板中是什么
?
???
Spring
給我們提供了
Hibernate
業務的模板。模板有什么,我們為什么需要它?答案就在我們的
SimpleEventDao
中的
create
方法中。
--------------------------------------------------------------------------------------------
protected
?
void
?create(Event?event)?{
??? try
?{
??? ??? startOperation();
??? ??? session.save?(event);
??? ??? tx.commit();
??? }?
catch
?(HibernateException?e)?{
??? ??? handleException(e);
??? }?
finally
?{
??? ??? HibernateFactory.close(session);
??? }
}
--------------------------------------------------------------------------------------------
???
如果你注意了,方法中只調用一個真正的事件是:
save
()。其他的每一行,我們喜歡稱為“
excise
(稅)”。
excise
(稅)是我們為了工作不得不去做的額外的事情,這些事情不是真正的直接重要。它就像當你開車去上班,駕駛的實際行動才是重要的事情;開車庫門和倒車都是
excise
(稅)任務,它可以看成同等重要,也可以忽略或者自動完成。
??????
在程序中,
excise
(稅)是框架或者語言安全需要,你不得不編寫的代碼。一個典型的
excise
(稅)例子就是
Java
除去了內存管理。
Spring
能夠除去
Hibernate
和
JDBC
要求之下的資源管理
excise
(稅)的一部分。
??????
一般情況下,當你復制代碼,你能夠
refactor
出
一個方法或類。這里,因為復制代碼是商務方法周圍的
resource?
cleanup
(資源清理),使它更加復雜。現在有模板可用。導入
Spring
提供的類:
org.springframework.orm.hibernate3.HibernateTemplate
。它包含了所有資源處理代碼以致于你僅只要寫一個重要方法就可以了。我們的
create()
方法能夠這樣寫:
----------------------------------------------------------------------------------------------------------------------------------------------------
import
?org.hibernate.Hibernate;
import
?org.hibernate.SessionFactory;
import
?org.springframework.orm.hibernate3.HibernateTemplate;
protected
?
void
?create(Event?event)?{
??? SessionFactory?sf?
=
?HibernateFactory.getSessionFactory();
??? HibernateTemplate?template?
=
?
new
?HibernateTemplate(sf);
??? template.saveOrUpdate(event);
}
-------------------------------------------------------------------------------------------------------------------------
注意我們沒有做什么:
n????????
從
SessionFactory
獲得
session
n????????
開始事物
n????????
捕獲預期異常和將他轉變為非預期異常(
3.x
版本不需要,
2.x
則需要)
n????????
提交事物
n????????
將改變輸入數據庫
n????????
關閉
session
這里有許多事情我們不需要擔心,因為
HibernateTemplate
已經照顧到了。你或許注意到
HibernateTemplate
看上去像包裹
Session
的周圍。實際上,可以把
HibernateTemplate
理解為一個“聰明”的
Session
,它知道怎樣打開,關閉和在運行完清除。在之前默認的情況下,它使用的是方法級事務(
transaction per method
)模式。這是非常簡單的,但你將在后面看到你也能夠改事務的范圍。這里有兩個基本的方式和
Hibernatetemplate
互動:通過
convenience
mehod
(便利的方法)和重要的
callback
(回調)。
?
Convenience methods
(便利的方法)
???
簡單的事情將變得容易。在許多案例中,你想用
Session
(會話)做什么是非常重要的:執行保存,更新有力對象,或者運行
HQL
查詢。沒有一個需要禮儀來獲得完成。
HibernateTemplate
類提供了基本的方法,因此一行代碼就可以簡單的調用業務。這里有一些方法的簡單樣例:
---------------------------------------------------------------------------------------------
import
?com.manning.hq.ch07.Event;
import
?com.manning.hq.ch07.HibernateFactory;
import
?org.springframework.orm.hibernate3.HibernateTemplate;
import
?java.util.List;
SessionFactory?sessionFactory?
=
HibernateFactory.getSessionFactory();
//
?創建連接?SessionFactory?的模板
HibernateTemplate?template?
=
new
?HibernateTemplate(sessionFactory);
Event?event1?
=
?
new
?Event();
event1.setName(
"
Event?1
"
);
Event?event2?
=
?
new
?Event();
event2.setName(
"
Event?2
"
);
??? try
?{
??? ??? template.save?(event1);???
//
?在一個事務中保存?event
??? ??? template.save?(event2);
//
?加載一個?event
??? ??? Event?obj?
=
?(Event)?template.load(Event.
class
,?event1.getId());
??? ??? System.out.println(
"
Loaded?the?event
"
?
+
?obj.getName());
//
?找到所有的?event
??? ??? List?events?
=
?(List)?template.find(
"
from?Event
"
);
??? ??? System.out.println(
"
#?of?Events?
"
?
+
?events.size());
??? }?
finally
?{
??? ??? template.delete(event1);?
//
?刪除一個?event
??? ??? template.delete(event2);
??? }
---------------------------------------------------------------------------------------------
convenience
methods
(
便利的方法)是有代表性的正確的命名,就像
Session
(會話)中的方法一樣。他們能被
session
(會話)作為
one-for-one
(一對一)復位直接調用,沒有任何雜亂的
resource cleanup
(資源清除)代碼障礙。
?
Callback
(回調)
???
復雜的事情可能有好處。不是所有的在單個
transaction
(
事務)中的單個
query
(查詢)能夠輕松的減少業務。在這些業務中,
spring
提供了
Callback
接口。它允許你在將要執行的模板中編寫
callback
方法。例如,如果你想構建一個復雜的
query
(查詢),更新一些數據,然后保存,所有的都在一個業務中:
-------------------------------------------------------------------------------------------
import
?org.hibernate.HibernateException;
import
?org.springframework.orm.hibernate3.HibernateCallback;
import
?java.sql.SQLException;
import
?org.hibernate.Query;
import
?java.util.List;
import
?java.util.Iterator;
import
?com.manning.hq.ch07.Event;?
template.execute(
new
?HibernateCallback()?{
??? public
?Object?doInHibernate(Session?session)?
throws
?HibernateException,?SQLException?{
?? ??? Query?query?
=
?session.createQuery(
"
from?Event
"
);
??? ??? query.setMaxResults(
2
);
??? ??? List?events?
=
?query.list();
??? ??? for
?(Iterator?it?
=
?events.iterator();?it.hasNext();)?{
??? ??? ??? Event?event?
=
?(Event)?it.next();
??? ??? ??? event.setDuration(
60
);
??? ??? }
??? ??? return
?
null
;
??? }
})?;
---------------------------------------------------------------------------------------------
這里,
Callback
接口使用了匿名內部類,
HibernateCallback
,定義了一個單一的方法,
doInHibernate()
。你可以編寫方法主體,然后提交
HibernateCallback
對象給模板,然后執行。模板處理資源管理代碼,讓你只需要編寫任務的
query
(查詢)邏輯。
?
7.4.2
Beans
和 它們的
factories
(工廠)
你已經看到能夠程序化使用
Spring
去減少
resource
cleanup
(資源清除)代碼。另外i,它能過更好的組織項目。
Spring
的慣例的說法是“輕量級”容器。它勝任執行和配置簡單的
JavaBeans
。它基本扮演構建和配置你應用程序中的
beans
的工廠角色。意思是它能夠被使用去配置大多數現存的
architectures
(框架)和庫,包括
Hibernate
。
?
中心的配置文件
???
在這一點上,你將通過組合使用
hibernate.cfg.xml
文件(
declaratively
(聲明性的
))和使用靈活的編程方式,如HibernateFactory來配置Hibernate。Spring提供了另外一個方法整體聲明配置Hibernate。使用Spring的最大好處就是你能夠減少編程配置需要的元素。
?
Spring
能夠讀用一般配置格式編寫的
XML
文件。
XML
指定了怎樣連接不同對象,包括
DataSource
(數據源),
SessionFactory
和所有的
DAOs
。一旦你配置了文件,你就能夠將它作為查找
DAOs
的中心“票據交換所”使用。例如,在
classpath
的
root
(根部)創建一個叫
applicationContext.xml
文件。就像
listing7.3
所顯示那樣。
?
listing7.3 ApplicationContext.xml
,定義了
DataSource
(數據源),
SessionFactory
和
DAO
-------------------------------------------------------------------------------
??
<?
xml?version
=
"
1.0
"
?encoding
=
"
UTF-8
"
?>
? <!
DOCTYPE?beans?PUBLIC
??? ? "
-//SPRING//DTD?BEAN//EN
"
??? ? "
http://www.springframework.org/dtd/spring-beans.dtd
"
>
??? <
beans
>
??? ??? <
bean?id
=
"
dataSource
"
class
=
"
org.apache.commons.dbcp.BasicDataSource
"
????? ??? destroy
-
method
=
"
close
"
>
???①
??? ??? <
property?name
=
"
driverClassName
"
>
??? ??? ??? <
value
>
com.mysql.jdbc.Driver
</
value
>
??? ??? </
property
>
??? ??? <
property?name
=
"
url
"
>
??? ??? ??? <
value
>
jdbc:mysql:
//
localhost/events_calendar</value>
??? ??? </
property
>
??? ??? <
property?name
=
"
username
"
>
??? ??? ??? <
value
>
root
</
value
>
??? ??? </
property
>
??? ??? <
property?name
=
"
password
"
>
??? ??? ??? <
value
></
value
>
??? ??? </
property
>
??? ??? </
bean
>
??? ??? <
bean?id
=
"
factory
"
class
=
"
org.springframework.orm.hibernate3.LocalSessionFactoryBean
"
>
?②
??? ??? <
property?name
=
"
mappingResources
"
>
??? ??? ??? <
list
>
??? ??? ??? ??? <
value
>
com
/
manning
/
hq
/
ch07
/
Event.hbm.xml
</
value
>
??? ??? ??? ??? <
value
>
com
/
manning
/
hq
/
ch07
/
Location.hbm.xml
</
value
>
??? ??? ??? </
list
>
??? ??? </
property
>
??? ??? <
property?name
=
"
hibernateProperties
"
>
??? ??? ??? <
props
>
??? ??? ??? ??? <
prop?key
=
"
hibernate.dialect
"
>
org.hibernate.dialect.MySQLDialect
</
prop
>
??? ??? ??? ??? <
prop?key
=
"
hibernate.show_sql
"
>
false
</
prop
>
??? ??? ??? </
props
>
??? ??? </
property
>
??? ??? <
property?name
=
"
dataSource
"
>
???③
??? ??? ??? <
ref?bean
=
"
dataSource
"
/>
??? ??? </
property
>
??? ??? </
bean
>
??? ??? <
bean?id
=
"
eventDao
"
class
=
"
com.manning.hq.ch07.EventSpringDao
"
>
????④
??? ??? <
property?name
=
"
sessionFactory
"
>
?????⑤
??? ??? ??? <
ref?bean
=
"
factory
"
?
/>
??? ??? </
property
>
??? ??? </
bean
>
??? </
beans
>
-------------------------------------------------------------------------------
listing7.3
解釋
①
???
配置一個基本的數據源,它使用由
Hibernate
發布的
Apache Commons database connection
pool (DBCP)
。
②??????
配置一個
SessionFactory
,構建在
Spring
SessionFactory
wrapper, LocalSessionFactoryBean
。當
Spring
讀取這個文件時,它就構建一個
SessionFactory
。
SessionFactory
存儲在
Key factory
。
③???????
連接
SessionFactory
和數據源。
④???????
配置你的
EventSpringDao
和
eventDao
。
⑤???????
連接
DAO
和
session factory
。他允許
DAO
打開
session
和發布
queries
。
-------------------------------------------------------------------------------------------------------------------------
Listing 7.3
中
XML
配置文件列舉了所有能夠經常改變詳細內容。它完成了許多與
hibernate.cfg.xml
所作的相同的事情,也為我們構建了
SessionFactory
,這一點將在下一節看到。
?
構建
AppicationContext
???
你剛剛創建的
applicationContext.xml
詳細的描述了怎樣構建
session
工廠。它基本上可以一對一的替換你所看到的
HibernateFactory
使用的
hibernate.cfg.xml
。它定義了通常在
hibernate.cfg.xml
中的屬性和映射文件。在我們先前的示例代碼中,你需要構建
SessionFactory
,或者通過你的
EventDao
對象連接
SessionFactory
。
Spring
顛覆了這個概念。
Spring
為你構建了
EventDao
,你需要詢問
EventDao
的首選項,就像:
---------------------------------------------------------------------------------------------
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import
?com.manning.hq.ch07.Event;
ClassPathXmlApplicationContext?ctx?
=
?
new
ClassPathXmlApplicationContext(
"
applicationContext.xml
"
);
EventSpringDao?eventDao?
=
(EventSpringDao)?ctx.getBean(
"
eventDao
"
,?EventSpringDao.
class
);
Event?event?
=
?
new
?Event();
eventDao.saveOrUpdate(event);
--------------------------------------------------------------------------------------------
ClasspathXmlApplicationContext
看上去
在
classpath
中,因為配置文件的名字在
instruction
中已提供(
The
ClasspathXmlApplicationContext
looks in the classpath for the name of the
configuration file provided in the instructions.
)在這個案例中,
applicationContext.xml
在
classpath
的
root
(根)。你能夠從
application context
通過名字請求
bean
。
getBean
()方法有兩個參數:
bean
的名字(
eventDao
),你期望的類的類型(
EventSpringDao
)。
在此之下,
Spring
構建了
SessionFactory
并將所有的
Bean
連接在一起。我們更早的認識到
Spring
與
Javabean
一起工作。所有的在
applicationContext.xml
文件中的
<bean>
元素都需要
JavaBean
。這包括了象下面的
EventSpringDao
。
-------------------------------------------------------------------------------------------------------------------------
public
?
class
?EventSpringDao?
extends
?AbstractSpringDao{
??? public
?EventSpringDao(){}
??? public
?Event?find(Long?id){
??? ??? return
?(Event)?
super
.find(Event.
class
,?id);
??? }
??? //
?Other?methods?excluded
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
????????
另外更早的認識到的好處是,
Spring
提供了
org.springframework.orm.hibernate3.support.HibernateDaoSupport
,
應用
Dao
層的父型。她管理了
SessionFactory
和一些有用的方法去處理
Session
,
Logging
和
HibernateTemplate
。這有這些方法的示例:
-------------------------------------------------------------------------------
public
?
abstract
?
class
?HibernateDaoSupport?
implements
?InitializingBean?{
??? protected
?
final
?Log?logger;
??? private
?HibernateTemplate?hibernateTemplate;
??? public
?
final
?
void
?setSessionFactory(SessionFactory?sessionFactory);
??? public
?
final
?SessionFactory?getSessionFactory();
??? public
?
final
?
void
?setHibernateTemplate(HibernateTemplate?hibernateTemplate);
??? public
?
final
?HibernateTemplate?getHibernateTemplate();
? ??? ??? ????? ??? ??? ??? ??? ??? ?? ??? ??? protected
?
final
?Session?getSession()?
throws? ??? DataAccessResourceFailureException,?IllegalStateException;
??? protected?final?void?closeSessionIfNecessary(Session?session);
}
-----------------------------------------------------------------------------------
他提供了一些基本的方法,但我們選擇重寫
HibernateDaoSupport
對象,為了
提供更多的便利方法。
Listing7.4
顯示了改變的類。
Listing
7.4
你的應用的層的父型
DAOs
--------------------------------------------------------------------------------------------
package
?com.manning.hq.ch07;
import
?java.util.List;
import
?org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public
?
abstract
?
class
?AbstractSpringDao?
extends
?HibernateDaoSupport{
??? public
?AbstractSpringDao()?{?}
??? protected
?
void
?saveOrUpdate(Object?obj)?{
??? ??? getHibernateTemplate().saveOrUpdate(obj);
??? }
??? protected
?
void
?delete(Object?obj)?{
??? ??? getHibernateTemplate().delete(obj);
??? }
??? protected
?Object?find(Class?clazz,?Long?id)?{
??? ??? return
?getHibernateTemplate().load(clazz,?id);
??? }
??? protected
?List?findAll(Class?clazz)?{
??? ??? return
?getHibernateTemplate().find(
"
from?
"
?
+
?clazz.getName());
??? }
}
------------------------------------------------------------------
重點注意的是
AbstractSpringDao
使用它的父類
sessionFactory
成員。
HibernateDaoSupport
提供了
getter
和
setter
方法,
Spring
使用
setter
方法去連接
SessionFactory
。從
applicationContext
.xml
文件中調用這些行:
-------------------------------------------------------------------------------
<
bean?id
=
"
eventDao
"
?
class
=
"
com.manning.hq.ch07.EventSpringDao>
??? <
property?name
=
"
sessionFactory
"
>
??? ??? <
ref?bean
=
"
factory
"
?
/>
??? </
property
>
</
bean
>
------------------------------------------------------------------
這是調用
setSessionFactory
()
的片斷,通過我們配置的
SessionFactory
,我們叫它為工廠。你所看到的
AbstractSpringDao
是由
AbstractDao
進化而來,你可以從
find()
方法中完整的除去大多數資源管理代碼。
HibernateTemplate
和
HibernateDaoSupport
替代了被控制的一切。
?
創建注冊(
Creating a registry
)
我們最終是為了將所有集中到一起并創建中心的注冊,開發者能夠使用它直接獲得參數給
DAO
和
SessionFactory
。通過單一的類,
CalendarRegistry
,你能確保將來開發有一個顯示的,單一的,強壯類型的類來使用,不需要知道類的詳細內部機制。
Figure 7.3
顯示了怎樣將所有集中到一起。
使用
Spring
,你獲得了配置的好處,允許你輕松的交換數據資源,數據庫和添加新的對象成員。
Listing7.5
就是
CalendarRegistry
。
?
Figure
7.3 CalendarRegistry
圖表,獲得參數到
EventDao
?
Listing
7.5
CalendarRegistry
,
組織
Dao
的中心的類
-------------------------------------------------------------------------------------------------------------------------
package
?com.manning.hq.ch07;
import
?org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import
?org.hibernate.SessionFactory;
public
?
class
?CalendarRegistry?{
??? private
?
static
?ApplicationContext?ctx;
??? static
?{
??? ??? ctx?
=
?
new
?ClassPathXmlApplicationContext(
"
applicationContext.xml
"
);
??? }
??? private
?CalendarRegistry()?{}
??? public
?
static
?SessionFactory?getSessionFactory()?{
??? ??? return
?(SessionFactory)?ctx.getBean(
"
factory
"
,?SessionFactory.
class
);
??? }
??? public
?
static
?EventSpringDao?getEventDao()?{
??? ??? return
?(EventSpringDao)ctx.getBean(
"
eventDao
"
,?EventSpringDao.
class
);
??? }
}
-------------------------------------------------------------------------------------------------------------------------
正如你所見,
CalendarRegistry
是單例模式,但是因為它背后是
Spring
,你能夠輕松的實現底層的交換。它從
classpath
中裝載單一的靜態的
ApplicationContext
,然后使用它獲取參數。現在客戶端對象能夠在項目中任何地方獲得參數給
EventDao
,不需要知道關于
Spring
做了什么。
-------------------------------------------------------------------------------------------------------------------------
EventSpringDao?eventDao?
=
?CalendarRegistry.getEventDao();
eventDao.saveOrUpdate(event);
--------------------------------------------------------------------------------------------
?
更多的
Spring
工具
就像你所見,使用
Spring
能夠最大限度的簡化
Hibernate
的資源管理代碼。我們顯示了兩個你能夠使用的包含的級別。
HibernateTemplate
能夠被嵌入你的
DAO
中,或者使用
Spring
的輕量級容器去管理
DAO
。
???
Spring is a fairly straightforward framework, but we haven’t scratched the
surface of what it can do here.
(
Spring
還算一個直接的框架,但我們沒有就它能在這做什么過多的糾纏)
Spring
也支持事務
API
管理框架,
AOP
框架,一個
RuntimeException
框架,能夠攔截數據庫發出的大多數遲鈍的
SQLException
,將它轉變成更顯而易見和包羅萬象的
exception
。更多的信息,請查看實在又全面的
Spring
文檔。
?
7.5
總結
這一章的焦點在于提高組織你的應用程序代碼。我們集中了
HQL
,使用了
DAO
模式。我們通過加入其它的模式更提高了初始實現,層的父型,它允許增加更多的DAO,不需要復制過多的代碼到項目中新添加的對象成員。
???
這一章也探索了怎樣使用
Spring
,另一個流行的開源項目,管理
Hibernate
需要的
boilerplate
(樣板)資源管理代碼。
Spring
提供了標題選項,
HibernateTemplate
,
pluggable
,
ApplicationContext
。增加一個
CalendarRegistry
,
它提供了方法使用
Spring
獲得項目中的參數,不再需要去包含一個“單塊集成電路”了。
?
1
Hibernate
如此優秀,為什么還要用別的?在現實中,轉變
ORM
實現并不是微不足道的,
DAO
是容易產生漏洞的,提取那樣做的話就會有問題,將不能從應用程序中完全隱藏
Hibernate
。(
DAOs are leaky enough
abstractions that doing so probably won’t completely hide Hibernate from the
application.
)。
因此不需要放太多的精力去密封
DAO
層,在以后為了可能的某種目的要轉變
ORM
實現。
?
2
選自
《
Patterns
of Enterprise Application Architecture
》
,
作者:
Martin Fowler(Addision-Wesley
專家,
2003
)
?
3
選自在線文章“
Data
Access with Spring Framework
”,作者:
Juergen Hoeller
,
2003
年
7
月;
http://hibernate.bluemars.net/110.html
。
posted on 2005-12-21 22:21
千山鳥飛絕 閱讀(10515)
評論(6) 編輯 收藏 所屬分類:
Hibernate