程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使用Hibernate進行對象的關系映射

使用Hibernate進行對象的關系映射

編輯:關於JAVA

Hibernate是業界比較推崇的ORM(o/r mapping),目前的版本已經非常穩定和成熟,而且的他的文檔也極其豐富。

Http://www.jdon.com和http://www.chinaxp.com 這兩個技術網站對hibernate的討論很多也很精到。

這篇文章是一篇讓出學者入門的文章。現意譯如下,英文好的可以跳過直接閱讀原文。

--譯者按

在Java技術中有許多方法可以對數據進行持久化,持久層也是Java應用程序中最重要的部分之一。在當今關系型數據庫仍為如主流數據庫的時候,我個人認為持久層技術常常並沒有得到應用的重視。在這篇文章裡將涉及到幾個著名的以Java技術實現的持久性框架,隨後我們會談到一些最新的持久性框架譬如:Hibernate,讓我們就從那些著名的框架以極其通用APIs開是這次旅行吧。

1.JDBC

大多數Java開發員都是用JDBC來和數據庫進行通信,它可以通過DAO(Data Access Object)模式來進行改善和提高,然而,這種方式在大型應用程序中則會造成維護的"高消費"。不過大多數的開發員對於JDBC的操作以及其API都是非常熟悉,因此,我相信不管怎樣它仍然是目前最被廣泛應用的數據持久技術之一。( 不要著急"噩夢馬上就要結束了",譯者注)

2.EJB

據調查EJB通常是在數據持久技術上的第二個選擇,它是通過entity beans來對數據進行持久化,這聽起來就好像是Java持久框架界中的"銀單"一樣(真的有銀彈??),我的意思是在巨大的市場下(潛在的一塊大蛋糕?)。然而事實上並不是這樣的:首先你需要購買一個價位合理的EJB容器--J2EE應用服務器,采用開源項目的免費EJB容器是一種不錯的選擇:),比如JBOSS(恕我直言);其次全面采用entity bean需要花"大量"的時間來理解EJB規范。在采用EJB之前你通常想在熟練掌握它的API;再有就是,你需要知道在每一個容器除了ejb-jar.xml以外所專有的部署描述符,而且很多商業EJB容器的性能和技術支持也不敢恭維。對於JAVA開發員,在EJB中實現JDBC也比較復雜。EJB中最為被關注的可能是無狀態的會話BEAN(stateless-Session beans)和消息驅動BEAN(messaging driver beans)

3.更多持久框架

通過對前面兩種規范一陣"游移不定和躊躇"以後,你可能發現它們都不是完美的解決方案。JDO的出現似乎有了一些改觀,但是JDO1.0不論是從操作方式上還是其功能上對於Java開發員來說似乎"天還是灰蒙蒙的","而且沒有一個好的免費的產品",終究JDO1.0仍然沒有帶來太大改變,人們仍停留在用它來學習的階段,這種情況有待JDO的成熟來解決。(目前SUN已經加入JDOCENTORL.COM來著手JDO2.0,我們唯有等待那絲曙光了。)

那麼我們該怎麼做呢?如果你拋開主流於非主流的概念,你會發現你將有更多的選擇(也許是更好的?),如果是這樣,你不會不注意到這樣兩個名字"Hibernate"和"OJB",這兩種持久框架跟前面的提到的JDBC、EJB和JDO在某些方面有很大的不同,它們不需要任何容器,提供簡單易用並符合ODMG3-style APIs,而且它們免費、開源、有豐富的文檔和穩定的開發背景。

也許你所要做的僅僅是選擇?

4"冬眠"Hibernate

就讓我們現在開始吧,在這些"新鮮的"持久框架中我選擇了Hibernate,同時當初我也把OJB列入我的選擇之列,不過最後之所以選擇Hibernate的原因要歸功於它豐富的文檔,我知道有許多其它的人使用OJB也取得過成功。不管怎麼樣,Hibernate和OJB都可以讓你完全操縱持久層。

請訪問http://hibernate.blumears.net/4.html來獲得Hibernate的功能介紹。

下面我想首先說一下我的運行環境:Hibernate 2.0 RC1 (2.0 beta 5)+ Tomcat 4.1.18+WebSphere Application Server 5.0.1(請到相應的官方網站獲取各個的最新版本,譯者注),我的例子在其他的容器中也能正常的運行,不過我沒有進行這方面的測試。Hibernate網站有關於在Jboss下的配置信息,通常幾乎所有的應用服務器和數據庫都能整合Hibernate。

4.1 Mapping Relationships with Hibernate

下面我們開討論如何使用Hibernate進行對象的關系映射。我將介紹如何使用Hibernate進行"一對一"、"一對多"、"多對多"的映射。在我提供的代碼中只是為了測試而沒有考慮到更多的"模式啦規則啦"等等,這就說這裡的例子只是一個啟蒙,讓我們從代碼中學習Hibernate的API以及如何進行開發,請再你自己書寫的時候注意必要的模式和編碼規范。

然後我們看看我的例子中都有哪些對象,讓們開始為他們建模吧,如下圖:

下圖為數據表關系圖:

4.2 配置Hibernate

點擊此處獲得本文中的實例代碼,這樣你可以對本例有更深的了解。

為了運行實例,請確信你已經下載過Hibernate和log4j的最新發布包,同時也要把數據庫驅動放到classpath中。下載以後的壓縮包中有example_schema.ddl文件用來生成數據庫表。

接下來作者給我們講述了一個Hibernate.properties文件,它是在配置Hibernate的時候最先接觸到的,它在應用程序啟動的時候為我們進行初始化工作(譯者注:有了hibernate.cfg.xml,Hibernate.properties變的可有可無了,不是嗎?)

hibernate.connection.driver_class=COM.ibm.db2.jdbc.net.DB2Driver
hibernate.connection.url=jdbc:db2://server1/sample
hibernate.connection.username=db2admin
hibernate.connection.password=password
hibernate.default_schema=db2admin
hibernate.dialect=net.sf.hibernate.dialect.DB2Dialect
hibernate.show_sql=true

# The maximum number of active connections that can be allocated # from this pool at the same time, or zero for no limit.
hibernate.dbcp.maxActive 100
# Action to take in case of an exhausted DBCP statement pool
# ( 0 = fail, 1 = block, 2= grow)
hibernate.dbcp.whenExhaustedAction 1
hibernate.dbcp.maxWait 120000
# The maximum number of active connections that can remain
# idle in the pool, without extra ones being released, or zero
# for no limit.
hibernate.dbcp.maxIdle 10

# The SQL query that will be used to validate
# connections from this pool before returning them to the caller.
# hibernate.dbcp.validationQuery=TODO
## prepared statement cache
hibernate.dbcp.ps.maxActive 100
# Action to take in case of an exhausted DBCP statement
#pool ( 0 = fail, 1 = block, 2= grow)
hibernate.dbcp.ps.whenExhaustedAction 1
# The maximum number of milliseconds that the pool will
# wait (when there are no available connections) for a connection
# to be returned before throwing an exception, or -1 to
# wait indefinitely.
hibernate.dbcp.ps.maxWait 120000
hibernate.dbcp.ps.maxIdle 100

上邊的代碼中,首先指明了和數據連接有關的屬性元素:database driver、JDBC URL、用戶賬號和密碼、dialect("數據庫"方言、土語、地方話)等等,dialect為我們使用的每一個數據庫進行最佳優化,在Hibernate使用手冊中你可以到得到每一個數據庫的dialect.最後,hibernate.show_sql當設定為"真"的時候,我們可以在Hibernate的DEBUG信息中看到HQL在執行的時候的SQL語句。

剩下的屬性元素是用來配置連接池的,這裡使用的是用Jakarta DBCP(詳細信息到Jakarta官方網站查看)來實現連接池,同樣Hibernate也可以用其它的方式來實現此功能,如:C3PO(沒聽說過,呵呵。。)。詳細信息進入Hibernate文檔。

4.3 創建持久對象

在Hibernate運行環境搭起來以後,我們開始創建持久對象或是映射文件來開始我們的工作。(通常創建對象和創建映射文件做其一即可,另一個可以通過做好的來自動完成),這裡我們從創建持久對象開始,下面是完成以後的代碼,Hibernate所需要的"持久對象"符合我們經常寫的對象的規范,它們沒什麼差別:

package dbdemo;
import java.util.Date;
import java.util.Set;
/**
* @hibernate.class table="Users"
*
* @author MEagle
*
* Represents a User
*/
public class User {
private String userID;
private String userName;
private String password;
private String emailAddress;
private Date lastLogon;
private Set contacts;
private Set books;
private Address address;
/**
* @hibernate.property column="EmailAddress" type="string"
* @return String
*/
public String getEmailAddress() {
return emailAddress;
}
/**
* @hibernate.property column="LastLogon" type="date"
* @return Date
*/
public Date getLastLogon() {
return lastLogon;
}
/**
* @hibernate.property column="Password" type="string"
* @return String
*/
public String getPassword() {
return password;
}
/**
* @hibernate.id generator-class="assigned" type="string"
* column="LogonID"
* @return String
*/
public String getUserID() {
return userID;
}

/**
* @hibernate.property column="Name" type="string"
* @return String
*/
public String getUserName() {
return userName;
}
/**
* @param string
*/
public void setEmailAddress(String string) {
emailAddress = string;
}

/**
* @param string
*/
public void setLastLogon(Date date) {
lastLogon = date;
}
/**
* @param string
*/
public void setPassword(String string) {
password = string;
}
/**
* @param string
*/
public void setUserID(String string) {
userID = string;
}
/**
* @param string
*/
public void setUserName(String string) {
userName = string;
}

/**
* @hibernate.set role="contacts" table="Contacts"
* cascade="all" readonly="true"
* @hibernate.collection-key column="User_ID"
* @hibernate.collection-one-to-many class="dbdemo.Contact"
* @return java.util.Set
*/
public Set getContacts() {
return contacts;
}

/**
* @param set
*/
public void setContacts(Set set) {
contacts = set;
}

/**
* @hibernate.set role="books" table="Book_User_Link"
* cascade="all" eadonly="true"
* @hibernate.collection-key column="UserID"
* @hibernate.collection-many-to-many
* class="dbdemo.Book" column="BookID"
* @return java.util.Set
*/
public Set getBooks() {
return books;
}
/**
* @param set
*/
public void setBooks(Set set) {
books = set;
}
/**
* @hibernate.one-to-one class="dbdemo.Address"
* @return dbdemo.Address
*/
public Address getAddress() {
return address;
}
/**
* @param address
*/
public void setAddress(Address address) {
this.address = address;
}
}

4.4 Ant and XDoclet

如果你仔細看了上邊的代碼,你會發現它有一點和我們以前的"不太一樣",它javadoc中多了許多特定的javadoc,是的,那是Hibernatedoclet。它和Xdoclet是"姊妹篇",Xdoclet是這樣一種工具:它通過和Apache Ant一起來產生應用程序的部署描述符。因此除非你樂意書寫xml映射文件,否要就會用到xdoclet(但是我還是建議初學者還是要給自己些機會手寫**.hbm.xml)。 進入查看hibernatedoclet的詳細信息,下面我們看看,這裡是如果利用對象來產生映射文件的,下面,看看build.xml:

<!-- this file uses Apache Ant 1.5.3 beta 1 -->
<project name="Hibernate Example" default="about" basedir=".">
<!-- The location where your xdoclet jar files reside -->
<property name="xdoclet.lib.home" value="c:/java_api/xdoclet-1.2b3/lib"/>
<target name="clean" depends="init" description="removes all directories related to this build">
<delete dir="${dist}"/>
</target>
<target name="init" description="Initializes properties that are used by other targets.">
<property name="dist" value="dist"/>
</target>
<target name="prepare" depends="init,clean" description="creates dist directory">
<echo message="Creating required directories..."/>
<mkdir dir="${dist}"/>
</target>
<target name="hibernate" depends="prepare"
description="Generates Hibernate class descriptor files.">
<taskdef name="hibernatedoclet" classname="xdoclet.modules.hibernate.HibernateDocletTask"> <classpath>
<fileset dir="${xdoclet.lib.home}">
<include name="*.jar"/>
</fileset>
</classpath>
</taskdef>
<!-- Execute the hibernatedoclet task -->
<hibernatedoclet
destdir="."
excludedtags="@version,@author,@todo"
force="true"
verbose="true"
mergedir="${dist}">
<fileset dir=".">
<include name="**/dbdemo/*.java"/>
</fileset>
<hibernate version="2.0"/>
</hibernatedoclet>
</target>
<target name="about" description="about this build file" depends="init">
<echo message=" Use this format for the arguments:"/>
<echo message=" ant hibernate"/>
<echo message=""/>
</target>
</project>

下面試運行時模擬的一個結果:

C:\eclipse\workspace\HibernateExample>ant hibernate
Buildfile: build.xml
init:
clean:
[delete] Deleting directory C:\eclipse\workspace\HibernateExample\dist
prepare:
[echo] Creating required directories...
[mkdir] Created dir: C:\eclipse\workspace\HibernateExample\dist
hibernate:
[hibernatedoclet] Running <hibernate/>
[hibernatedoclet] Generating mapping file for dbdemo.Contact.
[hibernatedoclet] Generating mapping file for dbdemo.Book.
[hibernatedoclet] Generating mapping file for dbdemo.Address.
[hibernatedoclet] Generating mapping file for dbdemo.User.
BUILD SUCCESSFUL
Total time: 2 seconds
C:\eclipse\workspace\HibernateExample>

接下來然我們看看,"生成"了什麼樣的映射文件(*.hbm.xml):

<?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="dbdemo.User"
table="Users"
>
<id
name="userID"
column="LogonID"
type="string"
>
<generator class="assigned">
</generator>
</id>
<one-to-one
name="address"
class="dbdemo.Address"
cascade="none"
outer-join="auto"
constrained="false"
/>
<set
name="books"
table="Book_User_Link"
lazy="false"
inverse="true"
cascade="all"
sort="unsorted"
>
<key
column="UserID"
>
</key>
<many-to-many
class="dbdemo.Book"
column="BookID"
/>
</set>
<set
name="contacts"
table="Contacts"
lazy="false"
inverse="true"
cascade="all"
sort="unsorted"
>
<key
column="User_ID"
>
</key>
<one-to-many
class="dbdemo.Contact"
/>
</set>
<property
name="emailAddress"
type="string"
column="EmailAddress"
not-null="false"
unique="false"
/>
<property
name="lastLogon"
type="date"
column="LastLogon"
not-null="false"
unique="false"
/>
<property
name="password"
type="string"
column="Password"
not-null="false"
unique="false"
/>
<property
name="userName"
type="string"
column="Name"
not-null="false"
unique="false"
/>
<!--
To add non XDoclet properties, create a file named
hibernate-properties-User.xml
containing the additional properties and place it in your merge dir.
-->
</class>
</hibernate-mapping>

一旦創建完映射文件(放在classpath 中,並且和對象是"一對一"關系),你就可以通過Hibernate的接口和方法來操縱系統對象。

最後說一下,本文中例子下載包中的內容,每一個單獨的例子都有main方法來運行:第一個例子:HibernateDemo.java,增加兩個users,並且和address相關聯("一對一");第二個例子:HibernateDemoOneToMany.java,學習用Hibernate進行"一對多"映射;最後,第三個例子:HibernateDemoManyToMany.java,學習用Hibernate進行"多對多"映射。建議你按照順序運行,如果你沒有使用DB2 (e.g. sequences).的話,你也可以跟改數據庫。例子包中還有一個例子:HibernateDemoHQL利用前面的例子產生的數據來說明如何用HQL來操縱數據。

作者提供的例子中很簡單,但對於初學者卻是非常好的一個學習的機會(希望初學者對作者的代碼進行運行嘗試,有許多東西本文並沒有說,不過你可以通過作者的代碼得到答案)。希望你能在學習Hibernate的時候從此處得到些幫助。

你可以通過Mark Eagle([email protected])和作者聯系。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved