Hibernate入門

來源:互聯網
上載者:User
也許你聽說過Hibernate的大名,但可能一直不瞭解它,也許你一直渴望使用它進行開發,那麼本文正是你所需要的!在本文中,我向大家重點介紹Hibernate的核心API調用庫,並講解一下它的基本配置。

  看完本文後,我相信你對什麼是ORM(對像/關係映射)以及它的優點會有一個深刻的認識,我們先通過一個簡單的例子開始來展現它的威力。

   正如一些傳統的經典電腦文章大都會通過一個“hello,world”的例子開始講解一樣,我們也不例外,我們也將從一個相對簡單的例子來闡述 Hibernate的開發方法,但如果要真正闡述Hibernate的一些重要思想,僅僅靠在螢幕上列印一些字元是遠遠不夠的,在我們的樣本程式中,我們 將建立一些對象,並將其儲存在資料庫中,然後對它們進行更新和查詢。

  “Hello World”

   Hibernate應用程式定義了一些持久類,並且定義了這些類與資料庫表格的映射關係。在我們這個“Hello world”樣本程式中包含了一個類和一個對應檔。讓我們看看這個簡單的持久類包含有一些什嗎?對應檔是怎樣定義的?另外,我們該怎樣用 Hibernate來操作這個持久類。

  我們這個簡單樣本程式的目的是將一些持久類儲存在資料庫中,然後從資料庫取出來,並將其資訊本文顯示給使用者。其中Message正是一個簡單的持久類:,它包含我們要顯示的資訊,其原始碼如下:

  列表1 Message.Java 一個簡單的持久類

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;
 }
 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的id 、訊息本文、以及一個指向下一條訊息的指標。其中id屬性讓我們的應用程式能夠唯一的識別這條訊息,通常它等同於資料庫中的主鍵,如果多個Message 類的執行個體對象擁有相同的id,那它們代表資料庫某個表的同一個記錄。在這裡我們選擇了長整型作為我們的id值,但這不是必需的。Hibernate允許我 們使用任意的類型來作為對象的id值,在後面我們會對此作詳細描述。

  你可能注意到Message類的代碼類似於JavaBean的代碼風格,並且它有一個沒有參數的建構函式,在我們以後的代碼中我將繼續使用這種風格來編寫持久類的代碼。

  Hibernate會自動管理Message類的執行個體,並通過內部機制使其持久化,但實際上Message對象並沒有實現任何關於Hibernate的類或介面,因此我們也可以將它作為一個普通的Java類來使用:

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

   以上這段代碼正是我們所期望的結果:它列印“hello world”到螢幕上。但這並不是我們的最終目標;實際上Hibernate與諸如EJB容器這樣的環境在持久層實現的方式上有很大的不同。我們的持久類 (Message類)可以用在與容器無關的環境中,不像EJB必須要有EJB容器才能執行。為了能更清楚地表現這點,以下代碼將我們的一個新訊息儲存到數 據庫中去:

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)

   在以上的SQL語句中,MESSAGE_ID欄位到底被初始化成了什麼值呢?由於我們並沒有在先前的代碼中為message對象的id屬性賦與初始值, 那它是否為null呢?實際上Hibernate對id屬性作了特殊處理:由於它是一個對象的唯一標識,因此當我們進行save()調用時, Hibernate會為它自動賦予一個唯一的值(我們將在後面內容中講述它是如何產生這個值的)。

  我們假設你已經在資料庫中建立了一 個名為MESSAGE的表,那麼既然前面這段代碼讓我們將Message對象存入了資料庫中,那麼現在我們就要將它們一一取出來。下面這段代碼將按照字母 順序,將資料庫中的所有Message對象取出來,並將它們的訊息本文列印到螢幕上:

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();

   在以上這段代碼中,你可能被find()方法的這個參數困擾著:"from Message as m order by m.text asc",其實它是Hibernate自己定義的查詢語言,全稱叫Hibernate Query Language(HQL)。通俗地講HQL與SQL的關係差不多就是方言與普通話之間的關係,咋一看,你會覺得它有點類似於SQL語句。其實在find ()調用時,Hibernate會將這段HQL語言翻譯成如下的SQL語句:

select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID
from MESSAGES m
order by m.MESSAGE_TEXT asc

  以下就是運行結果:

1 message(s) found:
Hello World

  如果你以前沒有ORM(對象-關係映射)的開發經驗,那你可能想在代碼的某個地方去尋找這段SQL語句,但在Hibernate中你可能會失望:它根本不存在!所有就SQL語句都是Hibernate動態產生的。

   也許你會覺得還缺點什麼,對!僅憑以上代碼Hibernate是無法將我們的Message類持久化的。我們還需要一些更多的資訊,這就是映射定義表! 這個表在Hibernate中是以XML格式來體現的,它定義了Message類的屬性是怎樣與資料庫中的MESSAGES表的欄位進行一一對應的,列表 2是這個樣本程式的映射設定檔清單:

  列表2:樣本程式的對象-關係映射表

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="hello.Message" table="MESSAGES">
 <id name="id" column="MESSAGE_ID">
  <generator class="increment"/>
 </id>
 <property name="text" column="MESSAGE_TEXT"/>
 <many-to-one name="nextMessage" cascade="all" column="NEXT_MESSAGE_ID"/>
</class>
</hibernate-mapping>

   以上這個文檔告訴Hibernate怎樣將Message類映射到MESSAGES表中,其中Message類的id屬性與表的MESSAGE_ID字 段對應,text屬性與表的MESSAGE_TEXT欄位對應,nextMessage屬性是一個多對一的關係,它與表中的 NEXT_MESSAGE_ID相對應。

  相對於有些開源項目來說,Hibernate的設定檔其實是很容易理解的。你可以輕鬆 地修改與維護它。只要你定義好了持久類與資料庫中表欄位的對應關係就行了,Hibernate會自動幫你產生SQL語句來對Message對象進行插入、 更新、刪除、尋找工作,你可以不寫一句SQL語句,甚至不需要懂得SQL語言!

  現在讓我們做一個新的實驗,我們先取出第一個Message對象,然後修改它的訊息本文,最後我們再產生一個新的Message對象,並將它作為第一個Message對象的下一條訊息,其代碼如下:

  列表3 更新一條訊息

Session session = getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
// 1 is the generated id of the first message
Message 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();

  以上這段代碼在調用時,Hibernate內部自動產生如下的SQL語句:

select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID
from MESSAGES m
where m.MESSAGE_ID = 1

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

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

   當第一個Message對象的text屬性和nextMessage被程式修改時,請注意Hibernate是如何檢測到這種變化,並如何在資料庫中自 動對它更新的。這實際上是Hibernate的一個很有價值的特色,我們把它稱為“自動髒資料檢測”,Hibernate的這個特色使得當我們修改一個持 久對象的屬性後,不必顯式地通知Hibernate去將它在資料庫中進行更新。同樣的,當第一個Message對象調用setNextMessage() 方法將第二個Message對象作為它的下一條訊息的引用時,第二條訊息會無需調用save()方法,便可以自動地儲存在資料庫中。這種特色被稱為“級聯 儲存”,它也免去了我們顯式地對第二個Message對象調用save()方法之苦。

  如果我們再運行先前的那段將資料庫中所有的Message對象都列印出來的代碼,那它的運行結果如下:

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

  “Hello world”樣本程式現在介紹完畢。我們總算對Hibernate有了一個簡單的認識,下面我們將回過頭來,對Hibernate的主要API調用作一下簡要的介紹:

  理解Hibernate的架構

   當你想用Hibernate開發自己的基於持久層的應用時,第一件事情應當是熟悉它的編程介面。Hibernate的API介面設計得盡量簡潔明了,以 方便開發人員。然而實際上由於ORM的複雜性,它的API一般都不可能設計得很簡單。但是別擔心,你沒有必要一下子瞭解所有的Hibernate的API 介面。下面這張圖描述了Hibernate在應用程式層和持久層中的一些重要的介面類:

 

  在中,我們將應用程式層放在了持久層的上部,實際上在傳統的項目中,應用程式層充當著持久層的一個用戶端角色。但對於一些簡單的項目來說,應用程式層和持久層並沒有區分得那麼清楚,這也沒什麼,在這種情況下你可以將應用程式層和持久層合并成了一層。

  在中,Hibernate的介面大致可以分為以下幾種類型:

  · 一些被使用者的應用程式調用的,用來完成基本的建立、讀取、更新、刪除操作以及查詢操作的介面。這些介面是Hibernate實現使用者程式的商業邏輯的主要介面,它們包括Session、Transaction和Query。

  · Hibernate用來讀取諸如映射表這類設定檔的介面,典型的代表有Configuration類。

  · 回調(Callback)介面。它允許應用程式能對一些事件的發生作出相應的操作,例如Interceptor、Lifecycle和Validatable都是這一類介面。

  · 一些可以用來擴充Hibernate的映射機制的介面,例如UserType、CompositeUserType和IdentifierGenerator。這些介面可由使用者程式來實現(如果有必要)。

  Hibernate使用了J2EE架構中的如下技術:JDBC、JTA、JNDI。其中JDBC是一個支援關聯式資料庫操作的一個基礎層;它與JNDI和JTA一起結合,使得Hibernate可以方便地整合到J2EE應用伺服器中去。

   在這裡,我們不會詳細地去討論Hibernate API介面中的所有方法,我們只簡要講一下每個主要介面的功能,如果你想瞭解得更多的話,你可以在Hibernate的源碼包中的 net.sf.hibernate子包中去查看這些介面的原始碼。下面我們依次講一下所有的主要介面:

  核心介面

  以下5個核心介面幾乎在任何實際開發中都會用到。通過這些介面,你不僅可以儲存和獲得持久對象,並且能夠進行事務控制。

  Session介面

   Session介面對於Hibernate 開發人員來說是一個最重要的介面。然而在Hibernate中,執行個體化的Session是一個輕量級的類,建立和銷毀它都不會佔用很多資源。這在實際項目 中確實很重要,因為在客戶程式中,可能會不斷地建立以及銷毀Session對象,如果Session的開銷太大,會給系統帶來不良影響。但值得注意的是 Session對象是非安全執行緒的,因此在你的設計中,最好是一個線程只建立一個Session對象。

  在Hibernate的設計者 的頭腦中,他們將session看作介於資料連線與交易管理一種中間介面。我們可以將session想象成一個持久對象的緩衝區,Hibernate能檢 測到這些持久對象的改變,並及時重新整理資料庫。我們有時也稱Session是一個持久層管理器,因為它包含這一些持久層相關的操作,諸如儲存持久對象至資料 庫,以及從資料庫從獲得它們。請注意,Hibernate 的session不同於JSP應用中的HttpSession。當我們使用session這個術語時,我們指的是Hibernate中的session, 而我們以後會將HttpSesion對象稱為使用者session。

  SessionFactory 介面

  這裡用到了一個設計模式――原廠模式,使用者程式從工廠類SessionFactory中取得Session的執行個體。

   令你感到奇怪的是SessionFactory並不是輕量級的!實際上它的設計者的意圖是讓它能在整個應用中共用。典型地來說,一個項目通常只需要一個 SessionFactory就夠了,但是當你的項目要操作多個資料庫時,那你必須為每個資料庫指定一個SessionFactory。
SessionFactory在Hibernate中實際起到了一個緩衝區的作用,它緩衝了Hibernate自動產生的SQL語句和一些其它的映射資料,還緩衝了一些將來有可能重複利用的資料。

  Configuration 介面

  Configuration介面的作用是對Hibernate進行配置,以及對它進行啟動。在Hibernate的啟動過程中,Configuration類的執行個體首先定位映射文檔的位置,讀取這些配置,然後建立一個SessionFactory對象。

  雖然Configuration介面在整個Hibernate項目中只扮演著一個很小的角色,但它是啟動hibernate時你所遇到的每一個對象。

  Transaction 介面

   Transaction介面是一個可選的API,你可以選擇不使用這個介面,取而代之的是Hibernate的設計者自己寫的底層交易處理代碼。 Transaction介面是對實際事務實現的一個抽象,這些實現包括JDBC的事務、JTA中的UserTransaction、甚至可以是CORBA 事務。之所以這樣設計是能讓開發人員能夠使用一個統一事務的操作介面,使得自己的項目可以在不同的環境和容器之間方便地移值。

  Query和Criteria介面

  Query介面讓你方便地對資料庫及持久對象進行查詢,它可以有兩種表達方式:HQL語言或本機資料庫的SQL語句。Query經常被用來綁定查詢參數、限制查詢記錄數量,並最終執行查詢操作。

  Criteria介面與Query介面非常類似,它允許你建立並執行物件導向的標準化查詢。

  值得注意的是Query介面也是輕量級的,它不能在Session之外使用。

  Callback 介面

  當一些有用的事件發生時――例如持久對象的載入、儲存、刪除時,Callback介面會通知Hibernate去接收一個通知訊息。一般而言,Callback介面在使用者程式中並不是必須的,但你要在你的項目中建立審計日誌時,你可能會用到它。

  一個重要的術語:Type

   Hibernate的設計者們發明了一個術語:Type,它在整個構架中是一個非常基礎、有著強大功能的元素。一個Type對象能將一個Java類型映 射到資料庫中一個表的欄位中去(實際上,它可以映射到表的多個欄位中去)。持久類的所有屬性都對應一個type。這種設計思想使用Hibernate有著 高度的靈活性和擴充性。

  Hibernate內建很多type類型,幾乎包括所有的Java基本類型,例如Java.util.Currency、Java.util.calendar、byte[]和Java.io.Serializable。

   不僅如此,Hibernate還支援使用者自訂的type,通過實現介面UserType和介面CompositeUserType,你可以加入自己的 type。你可以利用這種特色讓你的項目中使用自訂的諸如Address、Name這樣的type,這樣你就可以獲得更大的便利,讓你的代碼更優雅。自 定義type在Hibernate中是一項核心特色,它的設計者鼓勵你多多使用它來建立一個靈活、優雅的項目!

  策略介面

   Hibernate與某些其它開源軟體不同的還有一點――高度的可擴充性,這通過它的內建策略機制來實現。當你感覺到Hibernate的某些功能不 足,或者有某些缺陷時,你可以開發一個自己的策略來替換它,而你所要做的僅僅只是繼承它的某個策略介面,然後實現你的新策略就可以了,以下是它的策略接 口:

  · 主鍵的產生 (IdentifierGenerator 介面)

  · 本地SQL語言支援 (Dialect 抽象類別)

  · 緩衝機制 (Cache 和CacheProvider 介面)

  · JDBC 串連管理 (ConnectionProvider介面)

  · 交易管理 (TransactionFactory, Transaction, 和 TransactionManagerLookup 介面)

  · ORM 策略 (ClassPersister 介面)

  · 屬性存取原則 (PropertyAccessor 介面)

  · 代理對象的建立 (ProxyFactory介面)

  Hibernate為以上所列的機制分別建立了一個預設的實現,因此如果你只是要增強它的某個策略的功能的話,只需簡單地繼承這個類就可以了,沒有必要從頭開始寫代碼。

  以上就是Hibernate的一些核心介面,但當我們真正開始用它進行開發時,你的腦海裡可能總會有一個疑問:我是通過什麼方式,並從哪裡取得Session的呢?以下我們就解答這個問題。

  基礎配置

   現在回顧一下我們先前的內容:我們寫出了一個樣本程式,並簡要地講解了Hibernate的一些核心類。但要真正使你的項目運行起來,還有一件事必須要 做:配置。Hibernate可以配置成可在任何Java環境中運行,一般說來,它通常被用在2-3層的C/S模式的項目中,並被部署在服務端。在這種項 目中,Web瀏覽器、或Java GUI程式充當者用戶端。儘管我們的焦點主要是集中在多層web應用,但實際上在一些基於命令列的應用中也可以使用Hibernate。並且,對 Hibernate的配置在不同的環境下都會不同,Hibernate運行在兩種環境下:可管理環境和不可管理環境

  · 可管理環境――這種環境可管理如下資源:池資源管理,諸如資料庫連接池和,還有交易管理、安全定義。一些典型的J2EE伺服器(JBoss、Weblogic、WebSphere)已經實現了這些。

   · 不可管理環境――只是提供了一些基本的功能,諸如像Jetty或Tomcat這樣的servlet容器環境。一個普通的Java案頭應用或命令列程式也可 以認為是處於這種環境下。這種環境不能提供自動交易處理、資源管理或安全管理,這些都必須由應用程式自己來定義。

  Hibernate 的設計者們將這兩種環境設計了一個統一的抽象介面,因此對於開發人員來說只有一種環境:可管理環境。如果實際項目是建立在諸如Tomcat這類不可管理的環 境中時,那Hibernate將會使用它自己的交易處理代碼和JDBC串連池,使其變為一個可管理環境。
對於可管理的環境而言,Hibernate會將自己整合在這種環境中。對於開發人員而言,你所要做的工作非常簡單:只需從一個Configuration類中建立一個SessionFactory類就可以了。
  建立一個SessionFactory對象

   為了能建立一個SessionFactory對象,你必須在Hibernate初始化時建立一個Configuration類的執行個體,並將已寫好的映射 檔案交由它處理。這樣,Configuration對象就可以建立一個SessionFactory對象,當SessionFactory對象建立成功 後,Configuration對象就沒有用了,你可以簡單地拋棄它。如下是範例程式碼:

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

  在以上代碼中,Message.hb.xml這個對應檔的位置比較特殊,它與當前的classpath相關。例如classpath包含目前的目錄,那在上述代碼中的Message.hbm.xml對應檔就可以儲存在目前的目錄下的hello目錄中。

   作為一種約定,Hibernate的對應檔預設以.htm.xml作為其副檔名。另一個約定是堅持為每一個持久類寫一個設定檔,想一想如果你將所有 持久類的映射寫入一個單獨的設定檔中的話,那這個設定檔肯定非常龐大,不易維護。但這裡又出現了一個新問題:如果為每個類寫一個設定檔的話,這麼多 的設定檔應該存放在哪裡呢?

  Hibernate推薦你將每個對應檔儲存在與持久類相同的目錄下,並且與持久類同名。例如我們第一 個樣本程式中的Message持久類放在hello目錄下,那你必須在這個目錄下存放名為Message.hbm.xml的對應檔。這樣一個持久類都有 自己的一個對應檔,避免了出現像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)
.setProperties( System.getProperties() )
.buildSessionFactory();

   當然,Hibernate的對應檔還有很多其它的配置選項,比如資料庫連接的設定,或是能夠改變Hibernate運行時行為的一些設定。所有的設定 可能是非常龐雜的,足以讓你喘不過氣來,但是不必擔心,因為Hibernate為絕大多數值都設定了一個合理預設值,你只需要修改這些設定檔中的極小一 部分值。

  你可以通過以下幾種方式來修改Hibernate的系統配置參數:

  · 將一個Java.util.Properties執行個體作為參數傳給Configuration類的setProperties()方法。

  · 在Hibernate啟動時用Java –Dproperty=value的方式設定值。

  · 在classpath可以找到的路徑下建立一個名為hibernate.properties的設定檔。

  · 在classpath可以找到的路徑下建立一個名為hibernate.cfg.xml的檔案,並在其<property>標籤中定義屬性值。

  以上就是對Hibernate的一個大致介紹,如果你想知道得更多,那本文還是遠遠不夠的,我將陸續推出更多關於Hibernate的資料。但有一點是毫無疑問的:它的確是一個非常優秀的持久層解決方案!

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.