也許你聽說過Hibernate的大名,但可能一直不了解它,也許你一直渴望使用它進(jìn)行開發(fā),那么本文正是你所需要的!在本文中,我向大家重點(diǎn)介紹Hibernate的核心API調(diào)用庫,并講解一下它的基本配置。
看完本文后,我相信你對什么是ORM(對像/關(guān)系映射)以及它的優(yōu)點(diǎn)會有一個深刻的認(rèn)識,我們先通過一個簡單的例子開始來展現(xiàn)它的威力。
正如一些傳統(tǒng)的經(jīng)典計(jì)算機(jī)文章大都會通過一個“hello,world”的例子開始講解一樣,我們也不例外,我們也將從一個相對簡單的例子來闡述Hibernate的開發(fā)方法,但如果要真正闡述Hibernate的一些重要思想,僅僅靠在屏幕上打印一些字符是遠(yuǎn)遠(yuǎn)不夠的,在我們的示例程序中,我們將創(chuàng)建一些對象,并將其保存在數(shù)據(jù)庫中,然后對它們進(jìn)行更新和查詢。
“Hello World” |
“Hello world”示例程序讓您對Hibernate有一個簡單的認(rèn)識。 |
理解Hibernate的架構(gòu) |
介紹Hibernate接口的主要功能。 |
核心接口 |
Hibernate有5個核心接口,通過這幾個接口開發(fā)人員可以存儲和獲得持久對象,并且能夠進(jìn)行事務(wù)控制 |
一個重要的術(shù)語:Type |
Type是Hibernate發(fā)明者發(fā)明的一個術(shù)語,它在整個構(gòu)架中是一個非?;A(chǔ)、有著強(qiáng)大功能的元素,一個Type對象能將一個Java類型映射到數(shù)據(jù)庫中一個表的字段中去。 |
策略接口 |
Hibernate與某些其它開源軟件不同的還有一點(diǎn)――高度的可擴(kuò)展性,這通過它的內(nèi)置策略機(jī)制來實(shí)現(xiàn)。 |
基礎(chǔ)配置 |
Hibernate可以配置成可在任何Java環(huán)境中運(yùn)行,一般說來,它通常被用在2-3層的C/S模式的項(xiàng)目中,并被部署在服務(wù)端。 |
創(chuàng)建一個SessionFactory對象 |
要創(chuàng)建一個SessionFactory對象,必須在Hibernate初始化時創(chuàng)建一個Configuration類的實(shí)例,并將已寫好的映射文件交由它處理。 |
“Hello World”
Hibernate應(yīng)用程序定義了一些持久類,并且定義了這些類與數(shù)據(jù)庫表格的映射關(guān)系。在我們這個“Hello world”示例程序中包含了一個類和一個映射文件。讓我們看看這個簡單的持久類包含有一些什么?映射文件是怎樣定義的?另外,我們該怎樣用Hibernate來操作這個持久類。
我們這個簡單示例程序的目的是將一些持久類存儲在數(shù)據(jù)庫中,然后從數(shù)據(jù)庫取出來,并將其信息正文顯示給用戶。其中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屬性讓我們的應(yīng)用程序能夠唯一的識別這條消息,通常它等同于數(shù)據(jù)庫中的主鍵,如果多個Message類的實(shí)例對象擁有相同的id,那它們代表數(shù)據(jù)庫某個表的同一個記錄。在這里我們選擇了長整型作為我們的id值,但這不是必需的。Hibernate允許我們使用任意的類型來作為對象的id值,在后面我們會對此作詳細(xì)描述。
你可能注意到Message類的代碼類似于JavaBean的代碼風(fēng)格,并且它有一個沒有參數(shù)的構(gòu)造函數(shù),在我們以后的代碼中我將繼續(xù)使用這種風(fēng)格來編寫持久類的代碼。
Hibernate會自動管理Message類的實(shí)例,并通過內(nèi)部機(jī)制使其持久化,但實(shí)際上Message對象并沒有實(shí)現(xiàn)任何關(guān)于Hibernate的類或接口,因此我們也可以將它作為一個普通的Java類來使用:
Message message = new Message("Hello World"); System.out.println( message.getText() ); |
以上這段代碼正是我們所期望的結(jié)果:它打印“hello world”到屏幕上。但這并不是我們的最終目標(biāo);實(shí)際上Hibernate與諸如EJB容器這樣的環(huán)境在持久層實(shí)現(xiàn)的方式上有很大的不同。我們的持久類(Message類)可以用在與容器無關(guān)的環(huán)境中,不像EJB必須要有EJB容器才能執(zhí)行。為了能更清楚地表現(xiàn)這點(diǎn),以下代碼將我們的一個新消息保存到數(shù)據(jù)庫中去:
Session session = getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); Message message = new Message("Hello World"); session.save(message); tx.commit(); session.close(); |
以上這段代碼調(diào)用了Hibernate的Session和Transaction接口(關(guān)于getSessionFactory()方法我們將會馬上提到)。它相當(dāng)于我們執(zhí)行了以下SQL語句:
insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID) values (1, 'Hello World', null) |
在以上的SQL語句中,MESSAGE_ID字段到底被初始化成了什么值呢?由于我們并沒有在先前的代碼中為message對象的id屬性賦與初始值,那它是否為null呢?實(shí)際上Hibernate對id屬性作了特殊處理:由于它是一個對象的唯一標(biāo)識,因此當(dāng)我們進(jìn)行save()調(diào)用時,Hibernate會為它自動賦予一個唯一的值(我們將在后面內(nèi)容中講述它是如何生成這個值的)。
我們假設(shè)你已經(jīng)在數(shù)據(jù)庫中創(chuàng)建了一個名為MESSAGE的表,那么既然前面這段代碼讓我們將Message對象存入了數(shù)據(jù)庫中,那么現(xiàn)在我們就要將它們一一取出來。下面這段代碼將按照字母順序,將數(shù)據(jù)庫中的所有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()方法的這個參數(shù)困擾著:"from Message as m order by m.text asc",其實(shí)它是Hibernate自己定義的查詢語言,全稱叫Hibernate Query Language(HQL)。通俗地講HQL與SQL的關(guān)系差不多就是方言與普通話之間的關(guān)系,咋一看,你會覺得它有點(diǎn)類似于SQL語句。其實(shí)在find()調(diào)用時,Hibernate會將這段HQL語言翻譯成如下的SQL語句:
select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID from MESSAGES m order by m.MESSAGE_TEXT asc |
以下就是運(yùn)行結(jié)果:
1 message(s) found: Hello World |
如果你以前沒有ORM(對象-關(guān)系映射)的開發(fā)經(jīng)驗(yàn),那你可能想在代碼的某個地方去尋找這段SQL語句,但在Hibernate中你可能會失望:它根本不存在!所有就SQL語句都是Hibernate動態(tài)生成的。
也許你會覺得還缺點(diǎn)什么,對!僅憑以上代碼Hibernate是無法將我們的Message類持久化的。我們還需要一些更多的信息,這就是映射定義表!這個表在Hibernate中是以XML格式來體現(xiàn)的,它定義了Message類的屬性是怎樣與數(shù)據(jù)庫中的MESSAGES表的字段進(jìn)行一一對應(yīng)的,列表2是這個示例程序的映射配置文件清單:
列表2:示例程序的對象-關(guān)系映射表
<?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"> ?。糹d name="id" column="MESSAGE_ID"> ?。糶enerator 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字段對應(yīng),text屬性與表的MESSAGE_TEXT字段對應(yīng),nextMessage屬性是一個多對一的關(guān)系,它與表中的NEXT_MESSAGE_ID相對應(yīng)。
相對于有些開源項(xiàng)目來說,Hibernate的配置文件其實(shí)是很容易理解的。你可以輕松地修改與維護(hù)它。只要你定義好了持久類與數(shù)據(jù)庫中表字段的對應(yīng)關(guān)系就行了,Hibernate會自動幫你生成SQL語句來對Message對象進(jìn)行插入、更新、刪除、查找工作,你可以不寫一句SQL語句,甚至不需要懂得SQL語言!
現(xiàn)在讓我們做一個新的試驗(yàn),我們先取出第一個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(); |
以上這段代碼在調(diào)用時,Hibernate內(nèi)部自動生成如下的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 |
當(dāng)?shù)谝粋€Message對象的text屬性和nextMessage被程序修改時,請注意Hibernate是如何檢測到這種變化,并如何在數(shù)據(jù)庫中自動對它更新的。這實(shí)際上是Hibernate的一個很有價值的特色,我們把它稱為“自動臟數(shù)據(jù)檢測”,Hibernate的這個特色使得當(dāng)我們修改一個持久對象的屬性后,不必顯式地通知Hibernate去將它在數(shù)據(jù)庫中進(jìn)行更新。同樣的,當(dāng)?shù)谝粋€Message對象調(diào)用setNextMessage()方法將第二個Message對象作為它的下一條消息的引用時,第二條消息會無需調(diào)用save()方法,便可以自動地保存在數(shù)據(jù)庫中。這種特色被稱為“級聯(lián)保存”,它也免去了我們顯式地對第二個Message對象調(diào)用save()方法之苦。
如果我們再運(yùn)行先前的那段將數(shù)據(jù)庫中所有的Message對象都打印出來的代碼,那它的運(yùn)行結(jié)果如下:
2 message(s) found: Greetings Earthling Take me to your leader (please) |
“Hello world”示例程序現(xiàn)在介紹完畢。我們總算對Hibernate有了一個簡單的認(rèn)識,下面我們將回過頭來,對Hibernate的主要API調(diào)用作一下簡要的介紹: