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

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

原文連接:http://www.meagle.com:8080/hibernate.jsp

    Hibernate是業界比較推崇的ORM(o/r mapping),目前的版本已經非常穩定和成熟,而且的他的文檔也極其豐富。
    Http://www.jdon.comhttp://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以及如何進行開發,請再你自己書寫的時候注意必要的模式和編碼規範。

    然後我們看看我的例子中都有哪些對象,讓們開始爲他們建模吧,如下圖:
 hibernate1.gif
       下圖爲數據表關係圖:
  hibernate2.gif

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])和作者聯繫。

                                       Jplateau 9/9/2003    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章