這是最后一篇Java雜談了,以ORM框架的談論收尾,也算是把J2ee的最后一方面給涵蓋到了,之所以這么晚才總結出ORM這方面,一是筆者這兩周比較忙,另一方面也想善始善終,仔細的先自己好好研究一下ORM框架技術,不想草率的敷衍了事。  

        其實J2ee的規范指南里面就已經包括了一些對象持久化技術,例如JDO(Java       Data       Object)就是Java對象持久化的新規范,一個用于存取某種數據倉庫中的對象的標準化API,提供了透明的對象存儲,對開發人員來說,存儲數據對象完全不需要額外的代碼(如JDBC       API的使用)。這些繁瑣的工作已經轉移到JDO產品提供商身上,使開發人員解脫出來,從而集中時間和精力在業務邏輯上。另外,JDO很靈活,因為它可以在任何數據底層上運行。JDBC只是面向關系數據庫(RDBMS)JDO更通用,提供到任何數據底層的存儲功能,比如關系數據庫、文件、XML以及對象數據庫(ODBMS)等等,使得應用可移植性更強。我們如果要理解對象持久化技術,首先要問自己一個問題:為什么傳統的JDBC來持久化不再能滿足大家的需求了呢?  

        筆者認為最好是能用JDBC真正編寫過程序了才能真正體會ORM的好處,同樣的道理,真正拿Servlet/Jsp做過項目了才能體會到Struts、 Spring等框架的方便之處。很幸運的是筆者這兩者都曾經經歷過,用混亂的內嵌Java代碼的Jsp加Servlet轉發寫過完整的Web項目,也用 JDBC搭建過一個完整C/S項目的后臺。所以現在接觸到新框架才更能體會它們思想和實現的優越之處,回顧從前的代碼,真是丑陋不堪啊。^_^  

        回到正題,我們來研究一下為什么要從JDBC發展到ORM。簡單來說,傳統的JDBC要花大量的重復代碼在初始化數據庫連接上,每次增刪改查都要獲得 Connection對象,初始化Statement,執行得到ResultSet再封裝成自己的List或者Object,這樣造成了在每個數據訪問方法中都含有大量冗余重復的代碼,考慮到安全性的話,還要加上大量的事務控制和log記錄。雖然我們學習了設計模式之后,可以自己定義Factory來幫助減少一部分重復的代碼,但是仍然無法避免冗余的問題。其次,隨著OO思想深入人心,連典型的過程化語言Perl等都冠冕堂皇的加上了OO的外殼,何況是 Java中繁雜的數據庫訪問持久化技術呢?強調面向對象編程的結果就是找到一個橋梁,使得關系型數據庫存儲的數據能準確的映射到Java的對象上,然后針對Java對象來設計對象和方法,如果我們把數據庫的Table當作Class,Record當作Instance的話,就可以完全用面向對象的思想來編寫數據層的代碼。于是乎,Object       Relationship       Mapping的概念開始普遍受到重視,盡管很早很早就已經有人提出來了。  

        缺點我們已經大概清楚了,那么如何改進呢?對癥下藥,首先我們要解決的是如何從Data       Schema準備完美的映射到Object       Schema,另外要提供對數據庫連接對象生命周期的管理,對事務不同粒度的控制和考慮到擴展性后提供對XML、Properties等可配置化的文件的支持。到目前為止,有很多框架和技術在嘗試著這樣做。例如似乎是封裝管理得過了頭的EJB、很早就出現目前已經不在開發和升級了的Apache       OJB、首先支持Manual       SQL的iBATIS,還有公認非常優秀的Hibernate等等。在分別介紹它們之前,我還想反復強調這些框架都在試圖做什么:  

        畢竟Java       Object和數據庫的每一條Record還是有很大的區別,就是類型上來說,DB是沒有Boolean類型的。而Java也不得不用封裝類(Integer、Double等)為了能映射上數據庫中為null的情況,畢竟Primitive類型是沒有null值的。還有一個比較明顯的問題是,數據庫有主鍵和外鍵,而Java中仍然只能通過基本類型來對應字段值而已,無法規定Unique等特征,更別提外鍵約束、事務控制和級聯操作了。另外,通過Java       Object預設某Field值去取數據庫記錄,是否在這樣的記錄也是不能保證的。真的要設計到完全映射的話,Java的Static被所有對象共享的變量怎么辦?在數據庫中如何表現出來……  
我們能看到大量的問題像一座座大山橫在那些框架設計者們面前,他們并不是沒有解決辦法,而是從不同的角度去考慮,會得到很多不同的解決方案,問題是應該采取哪一種呢?甚至只有等到真正設計出來了投入生產使用了,才能印證出當初的設想是否真的能為項目開發帶來更多的益處。筆者引用一份文檔中提到一個健壯的持久化框架應該具有的特點:  
        A       robust       persistence       layer       should       support----  
        1.   Several       types       of       persistence       mechanism  
        2.   Full       encapsulation       of       the       persistence       mechanism.  
        3.   Multi-object       actions  
        4.   Transactions       Control  
        5.   Extensibility  
        6.   Object       identifiers  
        7.   Cursors:       logical       connection       to       the       persistence       mechanism  
        8.   Proxies:       commonly       used       when       the       results       of       a       query       are       to       be       displayed       in       a       list  
        9.   Records:     avoid     the     overhead       of       converting       database       records       to       objects       and       then       back       to       records  
        10.   Multi       architecture  
        11.   Various       database       version       and/or       vendors  
        12.   Multiple       connections  
        13.   Native       and       non-native       drivers  
        14.   Structured       query       language       queries(SQL)  

        現在來簡短的介紹一下筆者用過的一些持久化框架和技術,之所以前面強調那么多共通的知識,是希望大家不要盲從流行框架,一定要把握它的本質和卓越的思想好在哪里。  
        1.   Apache       OJB  
        OJB代表Apache       Object       Relational       Bridge,是Apache開發的一個數據庫持久型框架。它是基于J2ee規范指南下的持久型框架技術而設計開發的,例如實現了ODMG       3.0規范的API,實現了JDO規范的API,       核心實現是Persistence       Broker       API。OJB使用XML文件來實現映射并動態的在Metadata       layer聽過一個Meta-Object-Protocol(MOP)來改變底層數據的行為。更高級的特點包括對象緩存機制、鎖管理機制、 Virtual       代理、事務隔離性級別等等。舉個OJB       Mapping的簡單例子ojb-repository.xml:  

        <class-descriptor       class=”com.ant.Employee”       table=”EMPLOYEE”>  
                <field-descriptor       name=”id”       column=”ID”      
                    jdbc-type=”INTEGER”       primarykey=”true”       autoincrement=”true”/>  

                <field-descriptor       name=”name”       column=”NAME”       jdbc-type=”VARCHAR”/>  
      </class-descrptor>  

      <class-descriptor       class=”com.ant.Executive”       table=”EXECUTIVE”>  
                <field-descriptor       name=”id”       column=”ID”      
                jdbc-type=”INTEGER”       primarykey=”true”       autoincrement=”true”/>  

                <field-descriptor       name=”department”       column=”DEPARTMENT”       jdbc-type=”VARCHAR”/>  
                               
                <reference-descriptor       name=”super”       class-ref=”com.ant.Employee”>  
                        <foreignkey       field-ref=”id”/>  
                </reference-descriptor>  
      </class-descrptor>  

      2.   iBATIS  
      iBATIS最大的特點就是允許用戶自己定義SQL來組配Bean的屬性。因為它的SQL語句是直接寫入XML文件中去的,所以可以最大程度上利用到 SQL語法本身能控制的全部特性,同時也能允許你使用特定數據庫服務器的額外特性,并不局限于類似SQL92這樣的標準,它最大的缺點是不支持枚舉類型的持久化,即把枚舉類型的幾個對象屬性拼成與數據庫一個字段例如VARCHAR對應的行為。這里也舉一個Mapping文件的例子sqlMap.xml:  
        <sqlMap>  
            <typeAlias       type=”com.ant.Test”       alias=”test”/>  

            <resultMap       class=”test”       id=”result”>  
                    <result       property=”testId”       column=”TestId”/>  
                    <result       property=”name”       column=”Name”/>  
                    <result       property=”date”       column=”Date”/>  
            </resultMap>  

            <select       id=”getTestById”       resultMap=”result”       parameterClass=”int”>  
                select       *       from       Test       where       TestId=#value#  
            </select>  

            <update       id=”updateTest”       parameterClass=”test”>  
                Update       Tests       set       Name=#name#,       Date=”date”       where       TestId=#testId#  
            </update>  
        </sqlMap>  

      3.   Hibernate  
      Hibernate無疑是應用最廣泛最受歡迎的持久型框架,它生成的SQL語句是非常優秀。雖然一度因為不能支持手工SQL而性能受到局限,但隨著新一代 Hibernate       3.x推出,很多缺點都被改進,Hibernate也因此變得更加通用而時尚。同樣先看一個Mapping文件的例子customer.hbm.xml來有一個大概印象:  

      <hibernate-mapping>  
              <class       name=”com.ant.Customer”       table=”Customers”>  
              <id       name=”customerId”       column=”CustomerId”       type=”int”       unsaved-value=”0”>  
                      <generator       class=”sequence”>  
                              <param       name=”sequence”>   Customers_CustomerId_Seq   </param>  
                      </generator>  
            </id>  

            <property       name=”firstName”       column=”FirstName”/>  
            <property       name=”lastName”       column=”LastName”/>  

          <set       name=”addresses”       outer-join=”true”>  
                  <key       column=”Customer”/>  
                          <one-to-many       class=”com.ant.Address”/>  
          </set>  
 
          …  
            </class>

    </hibernate-mapping>  

        Hibernate有很多顯著的特性,最突出的就是它有自己的查詢語言叫做HQL,在HQL中select       from的不是Table而是類名,一方面更加面向對象,另外一方面通過在hibernate.cfg.xml中配置Dialect為HQL可以使得整個后臺與數據庫脫離耦合,因為不管用那種數據庫我都是基于HQL來查詢,Hibernate框架負責幫我最終轉換成特定數據庫里的SQL語句。另外 Hibernate在Object-Caching這方面也做得相當出色,它同時管理兩個級別的緩存,當數據被第一次取出后,真正使用的時候對象被放在一級緩存管理,這個時候任何改動都會影響到數據庫;而空閑時候會把對象放在二級緩存管理,雖然這個時候與數據庫字段能對應上但未綁定在一起,改動不會影響到數據庫的記錄,主要目的是為了在重復讀取的時候更快的拿到數據而不用再次請求連接對象。其實關于這種緩存的設計建議大家研究一下Oracle的存儲機制(原理是相通的),Oracle犧牲了空間換來時間依賴于很健壯的緩存算法來保證最優的企業級數據庫訪問速率。  

        以上是一些Mapping的例子,真正在Java代碼中使用多半是繼承各個框架中默認的Dao實現類,然后可以通過Id來查找對象,或者通過 Example來查找,更流行的是更具Criteria查找對象。Criteria是完全封裝了SQL條件查詢語法的一個工具類,任何一個查詢條件都可以在Criteria中找到方法與之對應,這樣可以在Java代碼級別實現SQL的完全控制。另外,現在許多ORM框架的最新版本隨著JDk       5.0加入Annotation特性都開始支持用XDoclet來自動根據Annotation來生成XML配置文件了。  

        筆者不可能詳細的講解每一個框架,也許更多的人在用Hibernate,筆者是從OJB開始接觸ORM技術的,它很原始卻更容易讓人理解從JDBC到 ORM的過渡。更多的細節是可以從官方文檔和書籍中學到的,但我們應該更加看中它們設計思想的來源和閃光點,不是盲從它們的使用方法。