sample chaptersample chapter - manning-content.s3 ... · 整合被管理 和非被管理的 ......

31

Upload: others

Post on 18-Oct-2019

6 views

Category:

Documents


0 download

TRANSCRIPT

  • D. Marsico SAMPLE CHAPTER

    D. Marsico SAMPLE CHAPTER

    D. Marsico SAMPLE CHAPTER

    D. Marsico SAMPLE CHAPTER

    D. Marsico SAMPLE CHAPTER

    DottieText BoxSAMPLE CHAPTER

  • Hibernate 實作手冊 n Actionby Christian Bauer

    andGavin King

    Chapter 2

  • 28

    介紹與整合 Hibernate

    這一章包含了

    ■ Hibernate 的運作和 "Hello World"

    ■ Hibernate 核心程式界面

    ■ 整合被管理和非被管理的界面

    ■ 進階的組態選項

  • "Hello World" 與 Hibernate 29

    雖然說了解 Java 應用程式中物件 /關聯的對應關係是很重要的,但是你可能更急

    著想看看 Hibernate 實際運作的情形,我們將從一個簡單的例子開始,來展示一

    些它的能力。

    就如同你可能知道的,所有程式設計的書都會從 "Hello World" 的例子開始。

    從這一章中,我們也會依循這個傳統,介紹一個由 Hibernate 組成相對簡單 "Hello

    World" 的例子。然而,只是在文字模式的視窗下顯示一個訊息並不能真正的展示

    Hibernate。反而,我們的程式會將新創造出來的物件儲存在資料庫之中,並且試

    著更新它們,進而從資料庫中作查詢,將它們取出來。

    這章將會建立之後章節的基礎。並且依據 "Hello World" 例子的標準之外,我

    們還會介紹核心 Hibernate 的 API 並且解釋如何在各種不同的環境,如 J2EE 的應

    用程式伺服器和獨立的應用程式中配置 Hibernate。

    2.1 "Hello World" 與 Hibernate

    Hibernate 應用程式定義永續類別是 " 對應 " 到資料庫的表格的。我們的 "Hello

    World" 範例程式就是由一個類別和一個對應檔所組成。在此一起看看一個簡單的

    永續類別應該看起來像什麼樣子,還有對應關係是如何被指定的,還有如何使用

    Hibernate 的永續類別的實例。

    我們範例程式的主題是將訊息能夠儲存到資料庫,並且可以取出並且展示。這

    個應用程式有一個簡單的永續類別 Message,用它來表達那些可被印出的訊息,

    Message 程式碼列在 2.1.

    package hello;public class Message { private Long id; private String text; private Message nextMessage; private Message() {} public Message(String text) { this.text = text; } public Long getId() { return id; }

    Listing 2.1 Message.java: 一個簡單的永續類別

    識別屬性

    訊息文字

    到另一訊息的參照

  • 30 CHAPTER 2介紹與整合 Hibernate

    private void setId(Long id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; }

    public Message getNextMessage() { return nextMessage; } public void setNextMessage(Message nextMessage) { this.nextMessage = nextMessage; }}

    Message 類別有三個屬性:識別屬性,訊息的內文,和一個指向另一個 Message的

    參照。這個識別屬性允許應用程式存取永續物件中資料庫的識別資訊,也就是主

    鍵值。如果兩個 Message的實例有相同的識別值,他們就表達著在資料庫內相同的

    列。我們選用 Long作為識別屬性的型態,但這不是必需的。如同你之後將見到的,

    Hibernate 事實上允許任何型態作為識別型態。

    你可能也注意到,所有 Message 類別的屬性都有 JavaBean 風格的屬性存取方

    式,這個類別也有個沒有任何參數的建構子,在我們範例中的永續類別都會像這

    樣的風格。

    Message類別的實例可以被Hibernate所管理(被永續儲存),但不一定非要這樣。

    既然 Message物件沒有實作任何 Hibernate 所指定的類別或界面,我們可以像其它

    Java 類別般的使用它:

    Message message = new Message("Hello World");System.out.println( message.getText() );

    上述的程式片斷正如同我們預期般的 "Hello World",在主控台下印出了 "Hello

    World"。它可能看起來像我們試圖著裝可愛;事實上,我們正試圖展示 Hibernate

    和其它的永續解決方案不同的一個重要的特色。我們的永續類別可以在任何的執

    行環境下執行,而不需要指定特定的 Container。當然,你是來這裡看 Hibernate

    本身的,就讓我們儲存一個新的 Message 到資料庫中。

  • "Hello World" 與 Hibernate 31

    Session session = getSessionFactory().openSession();Transaction tx = session.beginTransaction();Message message = new Message("Hello World");session.save(message);

    tx.commit();session.close();

    這段程式碼呼叫了 Hibernate 的 Session 和 Transaction 界面。( 我們馬上介紹

    getSessionFactory() 呼叫 )。其結果和執行了下面的 SQL 相似:

    insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID)values (1, 'Hello World', null)

    且慢,MESSAGE_ID 欄位被一個奇怪的值給初始化了。我們並沒有設定 Message 中

    的 id 屬性,我們應該要期待它被設成 null的,對嗎?事實上,id 屬性是特別的,

    它是一個識別屬性,它儲存了一個產生出來的獨一值。我們將在稍後討論這個值

    是如何產生出來,它將在 save() 呼叫時被 Hibernate 指定給 Message 的實例。

    以此例來說,我們假設 MESSAGE表格已經存在。在第 9章中,會介紹 Hibernate

    如何自動的產生你的應用程式所需要的表格,所需要的只是對應檔的資訊。(這會

    讓你減少更多需要手寫 SQL 的機會 )。當然,我們還是希望 "Hello World" 程式可

    以印出訊息到文字模式視窗下。現在我們已有資料儲存在資料庫中,且在剛才已

    經展示過了。下一個例子就是從資料庫中將資訊取出來,按照字母排列,並且印

    出來。

    Session newSession = getSessionFactory().openSession();Transaction newTransaction = newSession.beginTransaction();List messages = newSession.find("from Message as m order by m.text asc");System.out.println( messages.size() + " message(s) found:" );for ( Iterator iter = messages.iterator(); iter.hasNext(); ) { Message message = (Message) iter.next(); System.out.println( message.getText() );}newTransaction.commit();newSession.close();

    "from Message as m order by m.text asc" 字串是 Hibernate 查詢,表達了

    Hibernate 自己的物件導向 Hibernate Query Language(HQL)。這個查詢在 find()

    被呼叫時會內部轉換成下列的 SQL 語句:

  • 32 CHAPTER 2介紹與整合 Hibernate

    select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_IDfrom MESSAGES morder by m.MESSAGE_TEXT asc

    而上述的程式片斷列印如下:

    1 message(s) found:Hello World

    如果你之前從未使用過如同 Hibernate 般任何的物件關聯對應 (ORM) 工具,你或

    許會期待在程式或後設資料中看到 SQL 的語句。SQL 語句完全沒有出現,所有的

    SQL 都是在執行期間生成。(正確來說,對那些可重覆使用的 SQL 敘述句,是在程

    式啟動時生成 )。

    為了允許這個魔術發先,Hibernate 需要知到更多關於 Message類別永續化的資

    訊。這個資訊通常是由一個 XML 對應文件來提供。這個對應文件定義 Message類別

    中的屬性是如何對應到 Message 表格的欄位的其它訊息,就讓我們看列在 2.2 的對

    應文件。.

    Listing 2.2 一個簡單的 XML 對應

    注意 Hibernate 2.0跟 Hibernate 2.1 有著相同的 DTD!

  • "Hello World" 與 Hibernate 33

    這個對應文件告訴 Hibernate,Message 類別會被永續化到 MESSAGE 表格,識別屬

    性對應到 MESSAGE_ID 的欄位,text 屬性對應到名叫 MESSAGE_TEXT 的欄位,

    nextMessage 的屬性對應到 NEXT_MESSAGE_ID 這個欄位,且具有多對一的聯合多樣

    性。(現在先不要擔心其它的細節 )。

    正如同你所見,這個 XML 文件並不難懂。你可以手動的撰寫與維護。在第三章,

    我們將討論如何從內嵌 (embedded) 於原始碼中的註解來產生 XML 檔案。不論你選

    哪種方式,Hibernate 都有足夠的資訊完整產生使用於 Message 類別的實例與新

    增、修改、刪除和查詢的 SQL 語句。你不需要手動產生這些 SQL 敘述句。

    NOTE 許多開發者抱怨伴隨著 J2EE 開發時所碰到的 " 後設資料困境 "。有些人

    已經建議應該離開 XML 後設資料,而返回原始的 Java 程式碼。雖然在某

    些問題上我們贊成,但是 ORM 卻是個好例子說明著後設資料是確實有其必

    要性的。Hibernate 具有合理的預設值,能減少輸入,並且有成熟的文件

    型態宣告能夠用於自動完成或驗証的編輯器。你可以用許多不同的工具來

    產生後設資料。

    現在我們修改第一個訊息,並且當我們修改時,創造一個新的訊息並且結合到第

    一個訊息,請見 2.3.

    Session session = getSessionFactory().openSession();Transaction tx = session.beginTransaction();

    // 1 is the generated id of the first messageMessage message = (Message) session.load( Message.class, new Long(1) );message.setText("Greetings Earthling");Message nextMessage = new Message("Take me to your leader (please)");message.setNextMessage( nextMessage );tx.commit();session.close();

    這個程式在同一個交易中呼叫了下述三個 SQL:

    select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_IDfrom MESSAGES mwhere m.MESSAGE_ID = 1

    Listing 2.3 更新一個 Message

  • 34 CHAPTER 2介紹與整合 Hibernate

    insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID)values (2, 'Take me to your leader (please)', null)

    update MESSAGESset MESSAGE_TEXT = 'Greetings Earthling', NEXT_MESSAGE_ID = 2where MESSAGE_ID = 1

    注意 Hibernate 如何偵測第一個訊息物件中 text 和 nextMessage 屬性的改變而更

    新資料庫。我們正享受著 Hibernate 稱作自動不良檢驗。當一個物件在交易過程

    中被更改時,這個功能節省了我們額外要求 Hibernate 去更新資料庫的工作。同

    樣的,你也可以看到當第一個物件參照到第二個物件時,第二個物件會被永續化。

    這個功能叫作串聯式儲存。當新的物件要被永續化時只要它能被已經永續過的實

    例參照,這個功能可以節省了我們額外呼叫 save() 函數。另外要注意的是,SQL

    敘述句的順序跟我們設定屬性的順序不是一樣的。Hibernate 會使用一個高度發展

    的演算法來決定有效率的次序,並且在使用者可預測的情況下,還會避免違反資

    料庫外部參照值限制。這個功能叫作 "交易的延遲寫入。

    如果再次執行 "Hello World",得到輸出如下:

    2 message(s) found: Greetings Earthling Take me to your leader (please)

    這就是我們的 "Hello World" 應用程式,現在我們在手邊已有一些程式碼,我們

    將退後一步,以表達 Hibernate 主要的 API。

    2.2 了解架構

    使用者界面的學習是你想在應用程式中使用Hibernate以作為永續層的第一件事。

    API 設計主要的一個目標就是讓軟體元件之間的界面可以愈窄愈好。然而事實上,

    物件 / 關聯對應關係的 API 並不特別小。但是別擔心,你不需要一次就了解所有

    的 Hibernate 界面。圖 2.1 描繪出 Hibernate 界面在商業層和永續層中最重要的

    幾個角色。我們在此將商業層架在永續層之上,這是因為在傳統的分層式應用程

    式中,是由商業層是作為客戶端和使用者互動。有些簡單的應用程式或許不會清

    楚的區分商業邏輯和永續邏輯;這是允許的,因為這只是將架構圖簡化的結果。.

  • 了解架構 35

    圖 2.1 中 Hibernate 的界面可以大致區分如下:

    ■ 應用程式執行基本的CRUD和查詢等運算所需要呼叫的界面。這些界面主要是

    和 Hibernate 應用程式中商業與控制的邏輯相依的主要點。他們包含有

    Session, Transaction與 Query。

    ■ 被應用程式架構碼呼叫以組態 Hibernate 的界面,最重要的就是 Configura-

    tion類別。

    ■ 允許應用程式和發生在Hibernate的事件互動的Callback界面,例如 Inter-

    ceptor, Lifecycle 和 Validatable。

    ■ 允許延伸 Hibernate 強大的對應功能的界面,如 UserType, CompositeUser-

    Type和IdentifierGenerator。(如果有需要的話,)這些界面是由應用程式基

    礎元件所實作。

    Hibernate 使用許多現存的 Java API,例如 Java DataBase Connectivity(JDBC),

    Java Transaction API (JTA) 和 Java Naming and Directory Interface (JNDI)。

    JDBC 提供了對關聯式資料庫最基本層次的抽象化,幾乎只要資料庫有 JDBC 驅動程

    Figure 2.1 在一個層狀架構中 Hibernate API 的高階概觀

  • 36 CHAPTER 2介紹與整合 Hibernate

    式的,Hibernate 都能支援。JNDI 和 JTA 允許 Hibernate 可以和現有的 J2EE 應用

    程式伺服器整合。

    在這一節,我們不會提供 Hibernate API 存取函數的語法細節,只提及主要界

    面的角色。大部份這些界面,都可以在 net.sf.hibernate 這個套件中找到。接下

    來我們就簡略的依序看看每一個界面。

    2.2.1 核心界面

    這五個主要的界面幾乎每個 Hibernate 應用程式都會用到。使用這些界面,你能

    夠存取永續化的物件和控制交易。

    Session 界面

    Session界面是Hibernate應用程式所使用主要的界面。Session的實例是輕量且在

    創建和刪除時不是昂貴的,且由於你的應用程式任何時候都需要創建和刪除

    session,這一點是非常重要的。Hibernate 的 sessions 不是執行緒安全的且被設

    計成應該在同一時間內只有一個執行緒的。

    在 Hibernate 的概念中,session 是介於 connection 和 transaction 之中的,

    它可以想像成是單一工作元件的快取或收集。Hibernate 可以偵測物件的改變。有

    時我們會將Session叫成永續管理者因為它也是物件永續操作相關的界面以儲存或

    取出物件。在此需要注意 Hibernate 和 web 層的 HttpSession無關。當我們在這本

    書提及session,我們指的是Hibernate session。而當我們提到的是使用者session

    時,指的才是 HttpSession的物件。

    我們將在第四章第二節 "永續管理者 "中詳細描述 Session 界面。

    SessionFactory 界面

    應用程式是由 SessionFactory 來得到 Session 類別的實例。相對於 Session 界面

    這個就沒那麼有趣了。

    SessionFactory真的是不輕量!它是打算被許多程式的執行緒所共用的。通常所

    有應用程式們是共用一個 SessionFactory 的。舉例來說,SessionFactory 是在這

    些應用程式被初始化時被創建。然而,如果你想要用 Hibernate 來存取多個資料

    庫,個別的每一個資料庫都需要一個 SessionFactory。

  • 了解架構 37

    SessionFactory會快取Hibernate在執行時期所產生出來的SQL和其它所需要的

    對應後設資料。它也快取了這個交易時期的資料,而這些只有在下一個交易時也

    可能會再被利用。(只有在類別和收集對應的是第二階快取時才合適 )。

    Configuration 界面

    Configuration物件是用來組態和啟動Hibernate。應用程式使用 Configuration類

    別的實例指定對應的文件檔和 Hibernate 指定的屬性接著創建 SessionFactory。

    就算認為 Configuration 界面在整個 Hibernate 的應用程式中佔了相對少的部

    份,這是你在使用 Hibernate 時第一個會碰到的物件。2.3 節中有組態 Hibernate

    時會碰到問題的細節。

    Transaction 界面

    Transaction界面是非必要的API,Hibernate應用程式可以選擇不要使用這個界面,

    而使用自己的程式碼來管理交易。Transaction 將應用程式和底層的交易的實作抽

    象化。而這個交易實作可以是 JDBC 的交易,一個 JTA 的 UserTransaction 或者甚

    至是一般物件需求代理架構 (CORBA) 的交易,其好處是允許應用程式用一組一致

    的 API 控制交易。這同時也保持 Hibernate 應用程式在不同執行環境和執行容器

    上的可移植性。

    我們在整本書中使用 Hibernate 的 Transaction API。交易和 Transaction界面

    在第五章有解釋。

    Query 和 Criteria 界面

    Query 界面允許你在資料庫上執行查詢,並且控制查詢如何的進行。查詢是用 HQL

    或者是資料庫所提供的原生 SQL 方言。Query界面是用來接合查詢參數,控制查詢

    傳回結果的數目,最後執行該查詢。

    Criteria 界面也很類似;它允許你創建和執行物件導向的條件查詢。

    為了幫助你的應用程式不那麼冗長,Hibernate 提供了一些在 Session界面上的

    捷徑函數在一行程式執行查詢。但本書中不用這些捷徑,我們還是使用 Query 這

    個介面。

    Query界面是輕量且不能在創建的Session外面使用的。我們會在第七章"中描述

    Query 界面的特性。

  • 38 CHAPTER 2介紹與整合 Hibernate

    2.2.2 Callback 界面

    當有些有趣的事情發生在某物件上時,收回界面允許應用程式接受通告。舉例來

    說,當一個物件被載入,儲存或刪除時。Hibernate 應用程式不需要去實作這些收

    回,但是這些在實作一些一般性功能,如同查核資料時是很有幫助的。

    Lifecycle和 Validatable界面允許永續物件和它自己的永續生命週期相關的事

    件互動。永績生命週期包含著一個物件的 CRUD 等運算。Hibernate 小組深深被其

    它的 ORM 解決方案所共有的收回界面所影響。之後,他們了解由永績類別來實作

    和 Hibernate 指定的界面不是一個好主意,因為這樣會敗壞了永續類別而使其變

    成不可移植的程式碼。這些方式並沒有特別好處,在本書中就不討論了。

    Interceptor 界面引進是為了允許應用程式執行收回而不必特別要求永續類別

    實作 Hibernate 所指定的 API,實作 Interceptor 界面是被當作參數傳到永續實例

    的。我們將在第八章中討論一個範例。

    2.2.3 型態

    Hibernate 的架構中,一個基本且功能強大的元件就是 Type。Hibernate 的 Type

    物件對應到 Java 的型態,也對應到資料庫的欄位型態 (事實上,這個型態可能會

    跨過數個欄位 )。所有永續類別中的永續屬性,包含聯合,都有一個對應的

    Hibernate 型態。這個設計讓 Hibernate 極端的富有彈性和擴充性。

    內建的型態非常豐富,包含所有 Java 的原始型態,和許多 JDK 的類別,包含的

    型態如java.util.Currency, java.util.Calendar, byte[]和java.io.Serializable。

    更好的是,Hibernate 支援使用者自訂的客製型態。UserType和 CompositeUser-

    Type 這兩個界面允許你增加你自己的資料型態。你可以使用這個特色來使應用程

    式類別在處理如 Address,Name 和 MonetaryAmount 的型態時更方便與簡捷。自訂

    型態是 Hibernate 一個主要的特色,你也會被鼓勵有創意的使用它們。

    我們將在第六章第 6.1 節 " 了解 Hibernate 的型態系統 " 中介紹 Hibernate 的

    型態和使用者自訂型態。

  • 基本的組態 39

    2.2.4 延伸界面

    大部份 Hibernate 提供的功能都是可組態的,且允許你在數個內建的策略中作選

    擇。當內建的策略不足夠時,Hibernate 將讓你自訂自己實作的界面置入。而可擴

    充的項目包括:

    ■ 主鍵值的產生 (IdentifierGenerator界面 )

    ■ SQL 方言的支援 (Dialect抽象類別 )

    ■ 快取策略 (Cache和 CacheProvider的界面 )

    ■ JDBC 連接的管理 (ConnectionProvider 界面 )

    ■ 交易的管理 (TransactionFactory, Transaction 和 TransactionManager-

    Lookup 界面 )

    ■ ORM 策略 (ClassPersister界面階級 )

    ■ 屬性存取策略 (PropertyAccessor界面 )

    ■ 代理 (Proxy) 的創建 (ProxyFactory 界面 )

    Hibernate 在運送時對上述的每個界面至少實作了一個實例。所以如果你想要擴充

    現有的功能的話,你通常不需要從頭作起。你在實作自己的界面時可以參照範例

    程式可取得的原始碼。

    在我們開始用 Hibernate 寫任何的程式碼之前,我們必需先回答一個問題:如

    何才能得到一個 Session開始工作?

    2.3 基本的組態

    我們已經看過了範例程式,並且也檢視過了 Hibernate 的核心界面。你必需要了

    解如何組態,才能在應用程式中使用 Hibernate,Hibernate 可以在任何 Java 應

    用程式和發展環境下組態。一般說來,Hibernate 是用二層或三層式的客戶端 /伺

    服器端應用程式架構,而 Hibernate 只需要部署在伺服器上。客戶端通常是一個

    web 瀏覽器,但是 Swing 或者是 SWT 的客戶端程式也是常有的。雖然我們在本書集

    中在多執行緒的 web 應用程式,但是這種解析在其它的架構下也適用,包括文字

    模式命令列的應用程式。了解 Hibernate 在被管理的環境和非管理環境下的組態

    是重要的:

  • 40 CHAPTER 2介紹與整合 Hibernate

    ■ 被管理的環境─像資料庫連接池的設定、交易分界和安全設定是可 (在後設

    資料裡 )被指定的。一個 J2EE 的應用程式伺服器,如同 JBoss,BEA WebLogic

    或 IBM Websphere 都有實作標準 (J2EE 指定的 )Java 管理環境。

    ■ 非被管理的環境─經由執行緒共用池提供基本的並時管理。一般的 servlet

    容器,如同 Jetty 或 Tomcat,都有對 Java 的 web 應用程式提供非被管理的

    伺服器環境。一般獨立的桌上型環境或文字命令列的應用程式也視為非被管

    理的。非被管理的環境沒有提供自動的交易控制、資源管理或安全基礎架

    構。應用程式必需自行管理資料庫的連接和區分交易的邊界。

    Hibernate 試圖將部署時的環境抽象化。在非被管理的環境,Hibernate 管理交易和

    JDBC 的連接 ( 或者也可以委託應用程式碼處理這部份 )。而在被管理的環境下,

    Hibernate 整合了由 J2EE 容器管理的交易和資料來源。Hibernate 可以被組態成

    這兩種環境下部署。

    在這兩種環境下,第一件要作的事就是開始 Hibernate。實際上,這是非常簡單

    的,你需要從 Configuration下創建一個 SessionFactory。

    2.3.1 創建一個 SessionFactory

    為了要創建一個 SessionFactory,你必在應用程式初始化時先建立一個 Configu-

    ration 的實例並且使用它來設定對應檔的位址。當組態完之後,Conguration的實

    例就可以用來創建 SessionFactory。當 SessionFactory 創建出來之後,你就可以

    捨棄 Configuration類別了。

    下述的程式碼可以啟動 Hibernate

    Configuration cfg = new Configuration(); cfg.addResource("hello/Message.hbm.xml"); cfg.setProperties( System.getProperties() );SessionFactory sessions = cfg.buildSessionFactory();

    對應碼的位址 Message.hbm.xml是相對於應用程式類別路徑的根目錄。舉例來說,

    如果類別路徑是在現在這個目錄,則 Message.hbm.xml 必需放在 hello 這個目錄之

    下。XML 對應檔必須放在類別路徑。在本例中,我們也使用 java 虛擬機器上的系

    統屬性來設置其它組態的選項。(這可能是在應用程式碼之前或程式啟動的時就設

    置了 )。

  • 基本的組態 41

    Method chaining 是一種被許多 Hibernate 界面所支援的程式設計風格。

    這種風格在Smalltalk上比Java更流行,但是有些人認為這種方式比Java

    現行可接受的風格來得難以閱讀與除錯,但是,它在許多的情形下是很方

    便的。

    許多 Java 的開發者宣告了型態是 void 的 setter 或 adder 函數,在

    Smalltalk 中,沒有 void 的型態,而 setter 和 adder method 通常傳回

    收到的物件,這樣能使我們將之前的程式碼寫成如下的形式:

    SessionFactory sessions = new Configuration() .addResource("hello/Message.hbm.xml") .setProperties( System.getProperties() ) .buildSessionFactory();

    在上述的程式碼中,我們不需宣告 Configuration區域變數,我們會在一

    些程式碼中使用這種風格。但你若不喜歡這種風格,你可以自行不採用。

    如果你是用這種風格的,在每個函數的呼叫時用新的一行是較好的。不

    然,當你在用除錯器時會產生麻煩。

    基本上,Hibernate XML 對應檔是用 .hbm.xml 延伸檔名,另外一個習慣是,一個

    對應檔對應到一個類別,而不是將你所有的對應都列在同一個檔 ( 這被視為不好

    的風格 )。在 "Hello World" 的例子中,只有一個永續的類別,但假設我們有許多

    個永績的類別,若一個 XML 對應檔只處理一個類別,我們應該將這些對應檔案放

    在哪裡呢?

    Hibernate 的文件建議將對應檔和類別檔放在同一個目錄之下,舉例來說,

    Message類別的對應檔會叫作 Message.hbm.xml,且被放在 hello的目錄下。如果我

    們還有其它的永續類別,在它自己的對應檔中應該要被定義。我們建議你採用這

    種常規。通常單一巨大的後設資料被某些框架所建議採用,如Struts可發現struts-

    config.xml。而這就是造成 " 後設資料困境 " 的元凶。只要你想要,你可呼叫

    addResource() 載入多個對應檔。或者,如果你依照著之前提的習慣,你可以使用

    addclass()這個函數,將永續類別當作參數傳入:

    SessionFactory sessions = new Configuration() .addClass(org.hibernate.auction.model.Item.class) .addClass(org.hibernate.auction.model.Category.class) .addClass(org.hibernate.auction.model.Bid.class)

    METHODCHAINING

  • 42 CHAPTER 2介紹與整合 Hibernate

    .setProperties( System.getProperties() ) .buildSessionFactory();

    addClass()這個函數假設對應檔是以.hbm.xml結尾,且和對應的類別檔案是部署在

    一起的。

    我們已經展示過創建單一的 SessionFactory,大部份的應用程式都需要這種,

    如果你需要連接許多的資料庫,則你需要重復這個過程以建立另一個

    SessionFactory。每一個SessionFactory對應到一個資料庫且可以對該資料庫產生

    Sessions 和其一系列的類別對應。

    當然,除了指出對應文件之外,Hibernate 組態還有更多要作的事。你也需要指

    定資料庫的連接是如何獲得的,還有其它影響 Hibernate 在執行時的設定。看起

    來許多的屬性設定似乎勢不可擋 (完整的說明請見 Hibernate 的文件 ),但是別緊

    張,大部份已有合理的預設值,通常只有少量是必需處理的。

    你必需要使用下列任何一種方法來指定組態的選項

    ■ 將一個 java.util.properties的實例傳給 Configuration.setProperties()

    ■ 將系統的屬性使用 java -Dproperty=value傳入

    ■ 將一個 hibernate.properties的檔案放在類別路徑

    ■ 將 hibernate.cfg.xml內 元素放在類別路徑內

    第一個和第二個選項除非是快速測試或者是原型測試,不然很少使用,大部份的

    應用程式需要一個固定的設置檔。hibernate.properties和 hibernate.cfg.xml兩

    個檔都提供了相同的功能:組態 Hibernate。可以依照你對語法的喜好自行選擇。

    你甚至可以兩種都一起用,而可以在開發時期和部署時期有不同的設置,你會在

    這章的之後看到。

    另外一個較少使用的選項是當 Hibernate 應用程式由 SessionFactory 開啟

    Session 時提供一個 JDBC 的 Connection( 例如,可以呼叫 sessions.

    openSession(myConnection))。使用這個選項時,你不用給定任何的資料庫連接屬

    性。對於可使用資料庫連接架構的新的應用程式,我們不建議採用這種方式 ( 例

    如,應該是由 JDBC 連接池或者是應用程式伺服器的資料來源中取得 )。

    所有的組態選項,資料庫的連接設定是最重要的。這種在被管理和非被管理的

    環境下是不同的,我們必需分開兩種來討論,首先來看非被管理的情形。

  • 基本的組態 43

    2.3.2 在非被管理下的組態

    在一個如同 servlet 容器這種非被管理的環境下,應用程式需要負責獲得 JDBC 的

    連接。Hibernate 屬於這個應用程式的一部份,所以是負責接受這些連接。由你告

    訴 Hibernate 如何得到 ( 或者是創建一個新的 )JDBC 連接。一般說來並不鼓勵在

    每次和資料庫互動時都建立連接。反而,Java 應用程式應該使用 JDBC 連接池的方

    式。用連接池有三種理由:

    ■ 獲得一個新的連接是昴貴的。

    ■ 維持許多閒置不用的連接是昴貴的。

    ■ 對某些驅動程式而言,建立預備敘述也是昴貴的。

    圖 2.2 顯示出 JDBC 連接池在網頁應用程式執行時期所扮演的角色。既然非被管理

    的環境並沒有實作連接池, 應用程式應該實作自己的連接池演算法或者是依靠第

    三方所提供的函式庫如 C3P0 的連接池。一般說來,在沒有 Hibernate 的情形下,

    應用程式會呼叫連接池來得到 JDBC 的連線,進而執行 SQL 敘述句。

    在有 Hibernate 時,情況就不同了,它就像是 JDBC 連接池的客戶端,如圖 2.3 所

    示。應用程式使用 Hibernate 的 Session和 Query的 API 來作永續的運算,且只要

    管理資料庫的交易,理想的情況下是使用 Hibernate 的 Transaction API。

    使用連接池

    Hibernate定義了一個外掛模組架構允許整合任何連接池。然而,內建的是用C3P0,

    所以我們採用它。Hibernate 會依據給定的屬性建立組態池。一個使用 C3P0 的

    hibernate.properties檔案如 2.4 所示

    Non-Managed Environment

    Database

    Connection Pool

    User-managed JDBC connections

    JSP

    main()Servlet

    Application

    Figure 2.2 JDBC 在非被管理環境下 JDBC 連接池

  • 44 CHAPTER 2介紹與整合 Hibernate

    hibernate.connection.driver_class = org.postgresql.Driverhibernate.connection.url = jdbc:postgresql://localhost/auctiondbhibernate.connection.username = auctionuser hibernate.connection.password = secret hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect hibernate.c3p0.min_size=5 hibernate.c3p0.max_size=20 hibernate.c3p0.timeout=300 hibernate.c3p0.max_statements=50 hibernate.c3p0.idle_test_period=3000

    這樣的程式碼指出了下列的訊息,由第一行列出如下:

    ■ 實作了JDBC Driver類別的名字(驅動程式所提供的JAR必須放在應用程式的

    類別路徑之下 )

    ■ 一個指向主機和該資料庫連接的 JDBC 的 URL

    ■ 資料庫的使用者帳號

    ■ 該資料庫使用者的密碼

    ■ 這個資料庫的Dialect。雖然ANSI標準有在努力,但是SQL語法依然隨著資料

    庫的供應商而不同。所以你要指定一個 Dialect。Hibernate 已內建了所有

    熱門的 SQL 資料庫,而且新的 dialect 也可以被輕易的制定。

    ■ C3P0 最少必須準備的 JDBC 連接個數

    ■ 連接池中連接的最大個數,當這個數目被用完成,在執行時,系統會丟出例

    外。

    Listing 2.4 使用 hibernate.properties 的 C3P0 連接池設定

    JSP

    main()Servlet

    Application

    Hibernate

    Database

    Connection Pool

    Session

    Transaction

    Query

    Non-Managed Environment

    Figure 2.3 在非被管理環境下 Hibernate 與連接池

  • 基本的組態 45

    ■ 當一個閒置的連線超過連線中斷的時間(在這個例子,是5分鐘或300秒),會

    被移出連接池。

    ■ 會被快取的預備敘述的最大數目。快取預備敘述是 Hibernate 保持好效能最

    基本的要件。

    ■ 當一個連線自動變成有效的閒置秒數

    在表格中指定 hibernate.c3p0.*的屬性就是選擇 C3P0 作為 Hibernate 的連接池 (

    得到 C3P0 的支援,你不需要任何其他們任何的動作 )。C3P0 的特性比我們之前展

    示的更多,經由 Hibernate API 的手冊,在 net.sf.hibernate.cfg.Environment

    類別的 Javadoc 中有每個可組態的屬性。包含了和 C3P0 相關的設定,和其他

    Hibernate 直接支援的第三方連接池。

    其他像 Apache DBCP 和 Proxool 都是被支援的連接池。你在決定要用哪一個連

    接池之前應該每個都試一試。Hiernate 社群較傾向使用 C3P0 和 Proxool。

    Hibernate 也同時也含預設的連接池機制。這個連接池只適合 Hibernate 的測試

    和實驗。你不應該在正式的生產環境中使用。它不是被設計可以延伸到適合並時

    有許多需求的環境。而且它缺乏了在連接池中必需有的錯誤容忍的特性。

    開始 Hibernate

    你如何在這些屬性中開始Hibernate?你在hibernate.properties中宣告了這些屬

    性,你只要將這個檔案放在類別路徑下,它就會自動偵測而且在 Hibernate 第一

    次執行且創建一個 Configuration物件時讀入。

    讓我們總結你到目前學習組態的幾個步驟。(如果你要在一個非被管理的環境下

    執行,這時是你下載並安裝 Hibernate 的好時候 )

    1 下載並解壓縮你資料庫所用的 JDBC 驅動程式,它通常可以在資料庫廠商的

    網頁下載,將 JAR 檔放在應用程式類別路徑下,用同樣的方法處理

    hibernate2.jar。

    2 將 Hibernate 所相依的檔案放在類別路徑下,它們依著 Hibernate 散佈,並

    且在其 lib/的目錄下,請看 lib/README.txt的文字檔來了解表列中必要和

    非必要的函式庫。

    3 選定一個Hibernate支援的JDBC連接池且用一個屬性檔來組態,不要忘記指

    定 SQL 方言。

    4 將hibernate.properties檔放在類別路徑下以讓Configuration類別知道

    這些屬性。

  • 46 CHAPTER 2介紹與整合 Hibernate

    5 在你的程式中建立一個 Configuration 的實例,並且用 addResource() 或

    addClass()來載入XML的對應檔。由呼叫buildSessionFactory()從Config-

    uration來建立一個 SessionFactory。

    不幸的是,你現在還沒有任何的對應檔,如果你想要的話,你可以執行 "Hello

    World" 範例程式,或者跳過這一章,開始從第三章學習永續類別和對應。或者如

    果你要知道如何在被管理的環境下執行 Hibernate,就繼續讀下去。

    2.3.3 被管理環境下的組態

    被管理的狀態處理了許多交錯的考量,如同應用程式的安全性 (授權與認証 ),連

    接池和交易的管理。J2EE 應用程式伺服器就是典型的被管理環境。雖然應用程式

    伺服器通常都支援 EJB,就算你沒有使用 EJB 的 entity beans 你依然可以享受其

    它管理服務所帶來的好處。

    Hibernate 通常和 session 和 message-driven EJB 一起使用,如圖 2.4 所示。

    EJB 會呼叫 Hibernate 的 API,正如同 servlet, JSP,或者就如同獨立的應用程

    式:Session, Transaction和 Query。Hibernate 有關的程式在被管理和非被管理

    的境境下是完全可移植的。Hibernate 透明地處理了連線和交易的策略。

    應用程式伺服器用JNDI接合的資料來源接觸連接池,是 javax.jdbc.Datasource

    的實例。你需要提供完整的 JNDI 名稱來告訴 Hibernate 可以在 JNDI 的哪裡找到

    資料來源。一個這種情境的 Hibernate 組態檔如 2.5 所示 .

    EJB

    EJBEJB

    Application

    Hibernate

    Session

    Transaction

    Query

    TransactionManager

    Database

    ResourceManager

    Application Server

    Figure 2.4 在應用程式伺服器被管理環境下的 Hibernate

  • 基本的組態 47

    hibernate.connection.datasource = java:/comp/env/jdbc/AuctionDBhibernate.transaction.factory_class = \ net.sf.hibernate.transaction.JTATransactionFactory hibernate.transaction.manager_lookup_class = \ net.sf.hibernate.transaction.JBossTransactionManagerLookuphibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect

    這個檔案給定了資料來源的 JNDI 名稱。資料來源必需是由 J2EE 應用程式的部屬

    描述檔來組態;這是每家廠商不同的設定。再來,你必需要啟動 Hibernate 整合

    JTA。現在 Hibernate 必需要能定位 TransactionManager才能完整整合 container

    的交易機制。在 J2EE 的規格內沒有標準方法,但 Hibernate 支援所有著名的應用

    程式伺服器。最後當然,指定 Hibernate 的 SQL 方言是需要的。

    現在你已經正確地組態好所有的事情,在被管理和非被管理的情形下使用

    Hibernate 並不會有多大的不同。只要用對應檔創建一個 Configuration並且建造

    一個 SessionFactory。然而,需要額外考慮某些交易相關的環境設定。.

    Java 已經有標準的交易 API,JTA,是用來控制 J2EE 應用程式伺服器的被管理

    環境,這叫作由 container 所管理的交易 (CMT)。如果 JTA 的交易管理員存在,

    JDBC 的連接就會被這管理員完全控制,這和非被管理的環境不同,那些是由應用

    程式 (或連接池 )直接管理 JDBC 的連線和 JDBC 的交易。

    因此,被管理和非被管理的環境會使用不同的交易函數。既然 Hibernate 想在

    這些環境下可移植,它定義了交易控制的 API,Hibernate 的 Transaction 界面抽

    象化了底層的 JTA 或 JDBC 交易 ( 潛在的甚至包含 CORBA 交易 )。底層的交易策略

    是用 hibernate.connection.factory_class 屬性來設定, 且它可以是之下兩個值

    之一:

    ■ net.sf.hibernate.transaction.JDBCTransactionFactory 直接委任給 JDBC

    的交易。這種策略應該是在非被管理環境下和連接池一起用,若是在沒被給

    定任何方式下,系統預設使用這種方式。

    ■ net.sf.hibernate.transaction.JTATransactionFactory 委任給 JTA,這對

    CMT 來說是正確的方式,連線是由 JTA 給控制。注意如果 JTA 交易已經呼叫

    Listing 2.5 以 hibernate.properties檔來描述由 Container 所提供的資料來源的一個範例

  • 48 CHAPTER 2介紹與整合 Hibernate

    beginTransaction() 而在進行中,再來進行中的工作都會在這個交易時期中

    進行 (否則一個新的 JTA 交易就會開始 )。

    對於 Hibernate 中 Transaction API 和對應用程式的情境說明,可以看第五章 5.1"

    交易 "的說明。只要記得如果你要用 J2EE 應用程式伺服器工作,有兩步驟,設定

    Hibernate Transaction 界面的工廠類別至 JTA,再來依照你的應用程式伺服器所

    指定來宣告交易管理員的查訪。而查訪的策略只有在 Hibernate 的二階快取系統

    時才是需要的。但是就算不用快取時,設定它也不會有傷害。

    Tomcat 不是一個完整的應用程式伺服器,它只是一個 servlet 的

    container,雖然是一個 servlet container 卻有著一些應用程式伺服器

    才有的特性。如 Tomcat 的連接池就是可以和 Hibernate 一起使用的特性。

    Tomcat 內部使用 DBCP 作為連接池但是以 JNDI 的資料來源作為接口,就

    像一個真實的應用程式伺服器。要組態 Tomcat 的資料來源,你需要依照

    JNDI/JDBC 的文件修改 servlet.xml。你可以設定 hibernate.connec-

    tion.datasource來組態Hibernate來使用該連接池。記住Tomcat並沒有伴

    隨著交易管理員,所以這種情形就像先前所提在非被管理的環境之下。

    你應該已經有一個可以執行的 Hibernate 系統了,不論你是用簡單的 container 或

    是應用程式伺服器,建立一個永續類別 ( 可用一開始的 Message類別作例子 ),複

    製 Hibernate 和所需要的函式庫,加上 hibernate.properties 檔,再建立一個

    SessionFactory。

    下一節包含了進階的 Hibernate 組態選項。有些是被建議使用的,如記錄執行

    時期的 SQL 語句以供除錯用,或使用方便的 XML 組態檔而不是純文字檔。然而,你

    可以跳過這章而閱讀更多第三章有關永續類別,而之後再回來這裡。

    2.4 進階組態設定

    當你終於有了一個可以執行的 Hibernate 應用程式,了解所有 Hibernate 組態參

    數是值得的。這些參數讓你最佳化 Hibernate 執行時期行為的性能,特別是調整

    和 JDBC 互動的部份 (舉例來說如同使用 JDBC 的批次更新 )。

    我們現在不會拿這些細節來困擾你;在 Hibernate 的參考文件中有關於組態選

    項最好的參考文件。在前一節中,我們已展示給你需要開始使用的必需選項。

    HIBERNATE 與TOMCAT

  • 進階組態設定 49

    然而,有這裡有一個參數必需要強調的,不論何時你用 Hibernate 開發應用程

    式時,都會持續需要它。將 hibernate.show_sql參數調整到 true會將記錄所有產

    生的 SQL 並輸出到命令列提示符號下。你將會在偵錯,調整效能,或者是看看發

    生了什麼事時需要它。知道你的 ORM 層在作什麼是值得的,那就是為什麼 ORM 不

    會隱藏 SQL 於使用者。

    到此,我們假定你指定了組態參數檔是 hinbernate.properties,或者是用程式

    控制 java.util.Properties 的一個實例。還有第三個你會喜歡的選項,就是使用

    XML 作為組態檔。

    2.4.1 使用 XML 為底的組態

    你可以使用 XML 組態檔 ( 如 2.6 所展示的 ) 來完整的組態一個 SessionFactory。

    不像 hibernate.properties,只有組態的參數,hibernate.cfg.xml也可以指定對

    應文件的位址。許多使用者偏好使用這種方式來集中 Hibernate 的組態選項,而

    不是在應用程式碼中加 Configuration 的參數。.

    ?xml version='1.0'encoding='utf-8'?>

    true java:/comp/env/jdbc/AuctionDB net.sf.hibernate.dialect.PostgreSQLDialect net.sf.hibernate.transaction.JBossTransactionManagerLookup

    Listing 2.6 範例 hibernate.cfg.xml 組態檔

    B文件型態宣告

    名稱屬性C

    D屬性規格

    E 對應文件檔的宣告

  • 50 CHAPTER 2介紹與整合 Hibernate

    XML的語法分析器(parser)會使用文件型態中的宣告的Hibernate組態 DTD檔來

    檢驗這份文件。

    非必要的 name 屬性和 hibernate.session_factory_name是相等的,用作 JNDI 和

    SessionFactory的連接,在下一節中討論

    Hibernate 的屬性可以不是 hibernate 的字頭,不然屬性的名稱和值必需要相同於

    程式化組態的屬性。

    對應檔可以用應用程式資源或甚至是直接寫入檔名的方式指定。此處用的檔名是

    從我們線上拍賣程式的範例而來,我們會在第三章中介紹。

    現在你可以如此的初始化 Hibernate

    SessionFactory sessions = new Configuration() .configure().buildSessionFactory();

    等一下,Hibernate 是如何知道組態檔在哪裡 ?

    當 configurate() 呼叫時,Hibernate 會先在類別路徑下找名為 hiber-

    nate.cfg.xml的檔案。如果你希望使用不同的檔名或者要Hibernate找某個子目錄,

    你必需要傳入路徑至 configurate() 函數 :

    SessionFactory sessions = new Configuration() .configure("/hibernate-config/auction.cfg.xml") .buildSessionFactory();

    使用 XML 組態檔無疑比屬性或寫程式來組態屬性來得更舒服。事實上這樣作主要

    的好處是你可以有個類別對應檔放在應用程式的原始檔之外 ( 就算它只出現在輔

    助啟動的類別 )。舉例來說,你可以依照你的資料庫或者是環境 (開發或正式上線

    )的不同,使用程式化的方式來選擇不同組的對應檔 (或不同的組態選項 )。

    如果你同時有 hibernate.properties和 hibernate.cfg.xml在類別路徑下,XML

    的設定組態會蓋掉 properties 的組態。這通常在你想要保持一些基本屬性,但是

    又想要用 XML 檔在部署時覆寫一些時是有效的。

    你可能已注意到在 XML 的組態檔中 SessionFactory 也有一個 name 屬性。

    Hibernate 使用這個 name在創建之後自動將 SessionFactory連接到 JNDI。

    B

    C

    D

    E

  • 進階組態設定 51

    2.4.2 與 JNDI 結合的 SessionFactory

    在大部份的 Hibernate 應用程式,SessionFactory 應該只在應用程式初始化時被

    實例一次。這單一的實例應該被特定的程序中所有的程式碼使用,且所有 Session

    都應該被這單一的 SessionFactory 所創建。一個常問的問題是,Factory 應該被

    放在哪裡,且如何能夠不麻煩的被存取。

    在 J2EE 的環境之中,結合 JNDI 的 SessionFactory 是很容易在不同的執行緒和

    許多個熟悉 Hibernate 的元件中共用。當然,JNDI 不是可以得到 SessionFactory

    的唯一方式。還有很多可能的註冊樣式實作,包含 ServletContext或在 singleton

    樣式中的 static final變數。一個特別簡捷的方式是使用應用程式範圍的 IoC( 控

    制反轉 )的架構元件。然而,JNDI 是一個普遍的方式 (而且可以接觸 JMX 的服務,

    如你之後會看到的 )。我們會在第八章 8.1 節 " 設計分層的應用程式 "中討論一些

    其它的方式。

    NOTE Java 命名與目錄介面 (JNDI) API 允許物件在階層 ( 目錄樹 ) 狀結構中

    儲存和取出。JNDI 實作了註冊樣式。基礎結構的物件 ( 如交易本文,資

    料來源 ),組態設定 ( 環境設定,使用者登錄 ),或甚至是應用程式物件

    (EJB 的參照,物件的製造廠 ) 都可能用 JNDI 來連接。

    如果 hibernate.session_factory_name 是被設定到目錄的節點的話,SessionFac-

    tory會自動將自己連接到JNDI。如果你的執行環境沒有提供一個預設的JNDI本文(

    或者是預設的 JNDI 實作沒有支援 Referenceable 的實例 ) 你需要用 hiber-

    nate.jndi.url 和 hibernate.jndi.class這兩個屬性來指定 JNDI 的初始化本文。

    這裡的例子是 Hibernate 連接 SessionFactory 和名稱為 hibernate/Hibernate-

    Factory的程式,這是用Sun以檔案系統為基底的免費JNDI實作 , fscontext.jar:

    hibernate.connection.datasource = java:/comp/env/jdbc/AuctionDBhibernate.transaction.factory_class = \ net.sf.hibernate.transaction.JTATransactionFactoryhibernate.transaction.manager_lookup_class = \ net.sf.hibernate.transaction.JBossTransactionManagerLookuphibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialecthibernate.session_factory_name = hibernate/HibernateFactoryhibernate.jndi.class = com.sun.jndi.fscontext.RefFSContextFactoryhibernate.jndi.url = file:/auction/jndi

  • 52 CHAPTER 2介紹與整合 Hibernate

    當然,在這個任務上,你可以也用 XML 方式的組態。這個範例並不實際,因為大

    部份的應用程式伺服器會透過 JNDI 來提供連接池,且有一個可寫入預設本文 JNDI

    的實作。JBOSS 當然有,所以你可以省略最後兩個屬性,只指定 SessionFactory

    的名字。現在所有你需要作的,就是呼叫 Configuration.configure().buildSes-

    sionFactory() 一次來初始化這個連結。

    NOTE Tomcat 預設有一個唯讀的 JNDI 本文,在 servlet container 啟動後並

    不能由應用程式層面的程式碼來寫入。Hibernate 不能連接到這種本文,

    你必需使用一個完整本文的實作 ( 如同 Sun FS 本文 ),或者是省略

    session_factory_name在組態檔中的屬性以關閉JNDI和SessionFactory

    的連接。

    讓我們一起看看其它一些很重要且能記錄 Hibernate 運算的組態設定。

    2.4.3 日誌

    Hibernate( 和其它許多的 ORM 實作 )非同步的執行 SQL 敘述句。一個 INSERT敘述

    在呼叫 Session.save() 時通常並不會馬上被執行;一個 UPDATE 在應用程式呼叫

    Item.addBid() 時不會馬上執行。反而,SQL 敘述通常在交易結束時被執行。這種行

    為如之前所提及,為延後寫入。

    這個行為是追 ? 和除錯 ORM 程式有時並不直觀的証據。理論上,應用程式是有

    可能將 Hibernate 當成黑盒子而忽略它的行為。無疑的,Hibernate 應用程式不能

    查覺這種非同步性 ( 至少,不在重新排序直接 JDBC 的呼叫之外 )。然而,當你發

    覺你在除錯一個困難的問題,你需要能夠正確的知道 Hibernate 內部在幹什麼。既

    然 Hibernate 是開放原始碼的,你可以輕易的進入 Hibernate 的程式。這種方式

    在特殊場合下幫助很大,但是特別是在這種非同步的特性,Hibernate 很容易讓你

    迷失,你可以使用日誌來了解 Hibernate 的內部。

    我們已經提到了 hibernate.show_sql 組態參數,這通常是疑難排解的第一步。

    有時只有 SQL 還不夠,此時你需要鑽研更深一些。

    Hibernate 使用了 Apache commons-logging 來記錄了所有有趣的事件,此為一

    個很薄的抽象層,可以直接輸出到 Apache log4j( 如果你將 log4j.jar 放到類別路

    徑下 )或 JDK1.4 logging( 如果你使用 JDK1.4 或之後的版本,且 log4j 不存在時

  • 進階組態設定 53

    )。我們建議使用 log4j,因為它更成熟,更多人使用,且由許多主動的開發者所

    開發。

    要看到 log4j 的輸出,你需要有一個叫作 log4j.properties 的檔案並且放在類

    別路徑下 (就在 hibernate.properties或 hibernate.cfg.xml旁 )。底下的例子直

    接將日誌的訊息輸出到文字模式視窗下:

    ### direct log messages to stdout ###log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target=System.outlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} ➾ %5p %c{1}:%L - %m%n### root logger option ### log4j.rootLogger=warn, stdout ### Hibernate logging options ### log4j.logger.net.sf.hibernate=info ### log JDBC bind parameters ### log4j.logger.net.sf.hibernate.type=info ### log PreparedStatement cache activity ###log4j.logger.net.sf.hibernate.ps.PreparedStatementCache=info

    使用這個組態,在執行時期時你不會看到許多日誌訊息。將 log4j.log-

    ger.net.sf.hibernate的info換成debug會將Hibernate內部運作顯示出來。確保你

    不會在正式上線的環境下這麼作,因為寫日誌會比正常的資料庫存取還慢得多。

    最後,你有 hibernate.properties, hibernate.cfg.xml和 log4j.properties 組

    態檔。

    如果你的應用程式伺服器支援 Java 管理延伸,還有第三個方式來組態

    Hibernate。

    2.4.4 Java 管理延伸 (JMX)

    Java 世界有各種規格,標準。當然也有這些的實作。一個相對較新,且是在第一

    個版本,但是重要的規格,就是 Java 管理延伸 (JMX)。JMX 是有關管理系統元件,

    更好的說法是系統的服務。

    Hibernate 如何適合這個架構呢?當 Hibernate 部署到一個應用程式伺服器,可

    以使用其它的服務,如交易管理或資料庫交易池。但是為什麼不將 Hibernate 變

    成一個被管理的服務,讓別人可以依 ?並使用?讓 Hibernate 整合進 JMX,並且變

    成一個變管理的 JMX 元件是可能的。

  • 54 CHAPTER 2介紹與整合 Hibernate

    JMX 規格中定義了如下的元件:

    ■ JMX MBean 一個可重覆使用的元件 (通常是基礎結構 ),可以連接到某個管

    理的界面。

    ■ JMX container 調解一般對 MBean 的存取 (本地或遠端的 )。

    ■ (通常是一般的)JMX客戶端 可以用來透過JMX container管理任何的MBean

    一個支援 JMX( 如 JBoss) 的應用程式伺服器,就像是 JMX container 且允許任何

    的 MBean 組態並在應用程式啟動程序中作初始化。使用應用程式伺服器的管理主

    控台 (如同一個 JMX 客戶端 )來監控 MBean 是有可能的。

    MBean 可以被包裝成 JMX 服務,不只在可在支援 JMX 的應用程式伺服器間移植,

    更可以部署到一個正在執行中的系統 (熱部署 )。

    Hibernate 可以如同 JMX Mbean 般的被包裝與管理。Hibernate JMX 服務允許

    Hibernate 在應用程式伺服器起動時用 JMX 客戶端控制與組態。然而,JMX 元件不

    會自動整合進 container 管理的交易。所以,組態選項如 2.7 所列的 (一個 JBoss

    服務的部署描述檔 )看起來像是 Hibernate 在一個被管理的環境中的設定。

    jboss.jca:service=RARDeployer jboss.jca:service=LocalTxCM,name=DataSource auction/Item.hbm.xml, auction/Bid.hbm.xml java:/hibernate/HibernateFactory java:/comp/env/jdbc/AuctionDB net.sf.hibernate.dialect.PostgreSQLDialect net.sf.hibernate.transaction.JTATransactionFactory

    Listing 2.7 Hibernate jboss-service.xml JMX 部署描述檔

  • 結論 55

    net.sf.hibernate.transaction.JBossTransactionManagerLookup java:/UserTransaction

    HibernateService 依賴兩個 JMX 服務 : service=RARDeployer 和

    service=LocalTxCM,name=DataSource,兩個都是在 jboss.jca服務的領域名稱。

    Hibernate MBean 可在 net.sf.hinernate.jmx 的套件下找到,不幸的是,生命

    週期管理的函數,如開始和停止 JMX 服務並不在 JMX1.0 的規格內。所以 Hiberna-

    teService 內的 start()和 stop()指定是 JBoss 的應用程式伺服器。

    NOTE 如果你對 JMX 進階的使用有興趣,JBoss 是一個開放源始碼的開始點。所

    有的服務 ( 甚至 EJB container) 在 JBoss 中都被實作成 MBean 且可以由

    提供的主控台界面來管理。

    我們建議你在試著將 Hibernate 當成 JMX service 之前,先用程式化來組態

    Hibernate( 使用 Configuration物件 )。然而一些特性 (如同 Hibernate 應用程式

    的重新熱部署 )一旦他們在 Hibernate 實作出來,只有可能在 JMX 下實現。現在,

    最大的好處是 Hibernate 會隨著 JMX 自動啟動,所以不再需要在程式碼內創建

    Configuration且建造SessionFactory。一旦HibernateService已經部署且起動後,

    可以簡單的透過 JNDI 存取 SessionFactory。

    2.5 結論

    在這一章,我們由高層次看了 Hibernate 且在執行了簡單的 "Hello World" 範例

    之後看了其架構。甚至是在 JMX 下,你也看到如何在不同的環境用不同的技巧來

    組態 Hibernate。

    Configuraion和SessionFactory界面是Hibernate應用程式在被管理和非被管理

    環境下的進入點。Hibernate 提供了其餘的 API,如同 Transaction 界面,來橋接

    不同的環境,以允許永續的程式碼可移植於不同的環境。

  • 56 CHAPTER 2介紹與整合 Hibernate

    Hibernate 可以整合到幾乎所有的 Java 環境,它可以是 servlet, applet 或者

    是完整被管理的三層式客戶端 /伺服器端的應用程式。Hibernate 組態中最重要的

    元件是資料庫的資源 (連接組態 ),交易的策略與 XML 式的對應後設資料檔。

    Hibernate 組態界面已經被設計包含了許多易於了解的情境。通常,一個

    hibernate.cfg.xml的檔案,與一行程式碼就可以讓 Hibernate 起動和執行。

    沒有一些永續類別,和它們的 XML 對應文件的話就沒有太大的用處了。下一章

    會用於永續類別的撰寫和對應。你將會在真實的應用程式中,對不是顯而易見的

    物件 /關聯式對應來作永續物件的儲存和取出。