一、前言
JDBC是Java操作數據庫最常用的數據庫接口,它隔離了數據庫的復雜度,使程序員可以將主要精力放到程序邏輯上來。而JDBC也只是提供了和數據庫交互的簡單方式,如打開數據庫表,執行SQL語句等。這對於復雜的程序也需要編寫大量的代碼,因此,近幾年在Java數據庫領域出現了許多框架,這此框架重新對JDBC進行了更高層次的封裝。如早期的iBATIS。這個框架使用起來非常方便。它也沒有基於復雜的面向對象模型。也不工作在復雜的關系圖中。這種最初級的框架將數據庫和應用程序隔離開來。使程序員只操作邏輯數據庫。但由於這種框架不是基本面向對象和關系模型,在對付大規模應用程序時仍然捉襟見肘。基於這些不足,最近許多基於關系映射(ORM)的數據庫框架開始流行起來。Hibernate就是其中的佼佼者。ORM的基本思想是使用外鍵和適當的約束將抽象不同的數據庫。在最新的Hibernate3.x中,增強了對約束的控制,使用功能更強,也更容易使用。
在這些ORM框架剛興起的時候,有很多人認為ORM框架最多只是從多選擇之一。即使某個ORM框架做的非常出钯,將對象和數據庫之間映射的非常完美也是如此。他們認為直接使用SQL寫程序才是王道,而使用自動生成的SQL的效率低下,而且很不靈活。但隨著程序規模越來越大,這種想法越來越站不住腳。這主要並不只是因為直接使用SQL將產生大量的代碼,而是因為使用ORM框架,我們將操作一個完全不同的層:ORM層。直接使用SQL也可能產生其他的問題,如我們經常會遇到的N+1選擇問題。而且在連接很多表時,我們會一遍一遍地重復寫非常類似的SQL語句。如果我們使用Hibernate,這些問量就將蕩然無存。我們可以使非常簡單的HQL來完成上述復雜的問題。象Hibernate一樣ORM框架還應該能進行各種優化,以使操作達到最佳化。就目前來看,這些框架的優化功能已經越來越強,正在逐步取代用JDBC和SQL操作數據庫的方式。
雖然可以用ORM框架來編寫大多數程序,但有時也需要直接使用SQL來操作。也許Hibernate的開發團隊也意識到了這一點,也為Hibernate提供了直接執行SQL的功能。在早期的Hibernate版本中,解決方案是直接將JDBC連接暴露給用戶,這樣程序員就可以直接使用prepared statment來執行SQL了。但在新的Hibernate3.x中,這種情況已經被改變了。現在,Hibernate3.x可以不使用一條SQL編寫整個應用程序,而且這並不會影響Hibernate的靈活性,同時也可以使用Hibernate的所有其他特性。
二、Hibernate3.x功能演示
上面說了很多Hibernate的好處,也面就讓我們來體會一下Hibernate3.x在這方面的卓越表現。我們將使用一個簡單的Person-Employment-Oranization模型來說明。最簡單的類是Person,下面是它的定義:
<class name="Person" lazy="true">
<id name="id" unsaved-value="0">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>
看了上面的定義,可能我們會首先注意到三個手寫的SQL語句:INSERT、UPDATE和DELETE。其中的?將匹配上面所列的兩個屬性(這兩個屬性是id和name)。除了這個,這三條語句沒有什麼之處。
也許很多讀者最感興趣的是<loader>標簽。這個標簽定義了一個命名查詢。這個查詢在任何時候當我們使用get(),load()裝載person或使用惰性關聯獲取數據時都會執行它。一般來講,這個命名查詢應該是一個SQL語句,如下面如示:
<sql-query name="person">
<return alias="p" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {p.name}, ID AS {p.id} FROM PERSON WHERE ID=? FOR UPDATE
</sql-query>
注:一個本地的SQL查詢可能會返回多個"實體列",但本例比較簡單,只返回了一個實體。
Employment相對更復雜一點,而且並不是所有的屬性都包括在INSERT和UPDATE中。定義如下:
<class name="Employment" lazy="true">
<id name="id" unsaved-value="0">
<generator class="increment"/>
</id>
<many-to-one name="employee" not-null="true" update="false"/>
<many-to-one name="employer" not-null="true" update="false"/>
<property name="startDate" not-null="true" update="false"
insert="false"/>
<property name="endDate" insert="false"/>
<property name="regionCode" update="false"/>
<loader query-ref="employment"/>
<sql-insert>
INSERT INTO EMPLOYMENT
(EMPLOYEE, EMPLOYER, STARTDATE, REGIONCODE, ID)
VALUES (?, ?, CURRENT_DATE, UPPER(?), ?)
</sql-insert>
<sql-update>UPDATE EMPLOYMENT SET ENDDATE=? WHERE ID=?</sql-update>
<sql-delete>DELETE FROM EMPLOYMENT WHERE ID=?</sql-delete>
</class>
下面是命名查詢employment的定義:
<sql-query name="employment">
<return alias="emp" class="Employment"/>
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, ID AS {emp.id}
FROM EMPLOYMENT
WHERE ID = ?
</sql-query>
下面的Organization的定義:
<class name="Organization" lazy="true">
<id name="id" unsaved-value="0">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<set name="employments"
lazy="true"
inverse="true">
<key column="employer"/>
<one-to-many class="Employment"/>
<loader query-ref="organizationEmployments"/>
</set>
<loader query-ref="organization"/>
<sql-insert>
INSERT INTO ORGANIZATION (NAME, ID) VALUES ( UPPER(?), ? )
</sql-insert>
<sql-update>UPDATE ORGANIZATION SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM ORGANIZATION WHERE ID=?</sql-delete>
</class>
下面是兩個命名查詢的定義:
<sql-query name="organization">
<return alias="org" class="Organization"/>
SELECT NAME AS {org.name}, ID AS {org.id} FROM ORGANIZATION
WHERE ID=?
</sql-query>
<sql-query name="organizationEmployments">
<return alias="empcol" collection="Organization.employments"/>
<return alias="emp" class="Employment"/>
SELECT {empcol.*},
EMPLOYER AS {emp.employer}, EMPLOYEE AS {emp.employee},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, ID AS {emp.id}
FROM EMPLOYMENT empcol
WHERE EMPLOYER = :id AND DELETED_DATETIME IS NULL
</sql-query>
當我們看到這時,可能已經感覺到Hibernate的好處,那就是至少我們可以少維護數十行的Java代碼。而且將這些代碼轉換成了XML配置文件,這樣將使程序更加靈活和可維護。
下面的代碼是我們的最後的工作,一個命名查詢allOrganizationsWithEmployees的定義:
<sql-query name="allOrganizationsWithEmployees">
<return alias="org" class="Organization"/>
SELECT DISTINCT NAME AS {org.name}, ID AS {org.id}
FROM ORGANIZATION org
INNER JOIN EMPLOYMENT e ON e.EMPLOYER = org.ID
</sql-query>
雖然在現在為止還是有很多人喜歡直接使用SQL,這也包括我在內。但使用以Hbiernate為首的ORM框架可能會給我們帶來更多的好處,如它自動產生的SQL一般會比我們手寫的更優化。因此,Hibernate將成為軟件大工業時代的新的操作數據庫的標准。