註:關於Hibernate文章,請參考RunPC於128、129、130、132期,資料永續層解決方案Hibernate的相關介紹,作者為Mark Ho。
設定hibernatedoclet環境
在開發過程中Hibernate進行物件與關聯式資料的處理過程中,需要有一份映射文件,用以描述物件與關聯式資料的轉換關係,例如:資料型態的對應、資料欄位長度的限制、單向關聯、雙向關聯、一對多、一對一與多對多關聯等等的設定。一般而言映射文件命名通常為*.hbm.xml。
然而在實際開發上當物件作了修改或重構後,必須對映射文件進行維護。如此的開發方式相當的不直覺與不方便。並且學習映射文件的撰寫不是一件簡單的事。
因此我們換一個角度思考,如果在物件的設計中,順手將這些轉換關係的資訊寫在物件中,由物件編譯過程中直接幫我們產生相關的映射文件,則我們只要維護一份原始碼即可,當進行修改與重構的過程中,映射檔案的變更也將立即的反應出來。在此我們將使用XDoclet進行Code generate來達到這個目的。
註:有關於XDoclet相關觀念的文章,請參考RunPC於130、131期,XDoclet入門篇與進階篇,作者為歐宣修。
首先請參考筆者於148期中的Maven文章,使用Maven Genapp Plugin產生專案,並且參考設定 1將project.xml進行修改,在該檔中可以看到我們在Maven相依的plugin套件中加入了maven-xdoclet-plugin。接著參考設定 2對 project.properties加入設定參數。說明請參考表 1中關於hibernatedoclet的參數設定。最後再對maven.xml進行設定,參考設定 3中可知當我們執行java:compile這個goal前會先行運作xdoclet:hibernatedoclet協助我們產生Mapping file。
設定 1 project.xml
<?xml version="1.0" encoding="UTF-8"?>
<project>
<pomVersion>3</pomVersion>
<artifactId>hibernate-example</artifactId>
<groupId>hibernate-example</groupId>
<name>Example Hibernate Application</name>
<currentVersion>1.0</currentVersion>
<dependencies>
<dependency>
<groupId>xdoclet</groupId>
<artifactId>maven-xdoclet-plugin</artifactId>
<version>1.2.3</version>
<type>plugin</type>
</dependency>
…中間省略…
</dependencies>
…中間省略…
</project>
設定 2 project.properties
#####################################
# hibernatedoclet properties
#####################################
maven.xdoclet.hibernatedoclet.fileset.0=true
maven.xdoclet.hibernatedoclet.fileset.0.sourcedir=${maven.src.dir}/java
maven.xdoclet.hibernatedoclet.fileset.0.include=**/*.java
maven.xdoclet.hibernatedoclet.fileset.0.exclude=
maven.xdoclet.hibernatedoclet.hibernate.0=true
maven.xdoclet.hibernatedoclet.hibernate.0.Version=3.0
maven.xdoclet.hibernatedoclet.destDir=${maven.build.dest}
設定 3 maven.xml
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns:j="jelly:core"
xmlns:ant="jelly:ant">
<preGoal name="java:compile">
<attainGoal name="xdoclet:hibernatedoclet"/>
</preGoal>
</project>
表 1 maven-xdoclet-plugin設定
參數 | 說明 |
maven.xdoclet.hibernatedoclet.fileset.[index] | 設定hibernatedoclet引用的檔案集設定,index值為0~9共10組設定,也就是說最多可以針對10組路徑下的原始檔進行hibernatedoclet的Code generate,預設index=0為啟用。若指定的maven.xdoclet.hibernatedoclet.fileset.[index]未設定,則以下相對應的檔案集設定即無效。 |
maven.xdoclet.hibernatedoclet.fileset.[index].sourcedir | 指定檔案集原始碼放置路徑,預設為${maven.src.dir}/java |
maven.xdoclet.hibernatedoclet.fileset.[index].include | 指定檔案集引入檔案規則,預設為**/*.java |
maven.xdoclet.hibernatedoclet.fileset.[index].exclude | 指定檔案集排除檔案規則,預設不排除任何檔。 |
maven.xdoclet.hibernatedoclet.hibernate.[index] | 設定對maven.xdoclet.hibernatedoclet.fileset.[index]進行hibernatedoclet是否生效,預設為true,若為false則對指定的檔案集不執行hibernatedoclet。 |
maven.xdoclet.hibernatedoclet.hibernate.[index].Version | 設定對maven.xdoclet.hibernatedoclet.fileset.[index]進行hibernatedoclet所產生的*.hbm.xml檔案可使用於hibernate那個版本,預設為1.1版,目前可設定的值為目前有1.1、 2.0、2.1、3.0。 |
maven.xdoclet.hibernatedoclet.destDir | 所有檔案集產生出的*.hbm.xml放置的目的路徑。 |
建立Domain Object並產生Mapping File
當上一節的設定完成後,我們正式建立Domain Object,並試圖產生Hibernate所需要的Mapping file。這裏我們
是以POJO定義Domain Object,而POJO就是所謂的Plain Ordinary Java Object,字面上來講就是無格式普通Java 物件,簡單的可以理解爲一個不包含邏輯程式碼的值物件。
我們在此建立二個POJO,分別是User(使用者)與Role(所屬角色)作為開始。這時Maven專案架構應該如資料結構 1所示,圖 1展示了類別屬性與其set、get相關method。接著參考程式 1、程式 2分別在類別中加入<hibernatedoclet>Tag設定,在此我們簡單的了解一下相關屬性用途,若讀者希望了解更詳細的屬性設定,可參考XDoclet官方網站http://xdoclet.sourceforge.net/xdoclet/tags/hibernate-tags.html。
-
@hibernate.class:該Tag用於宣告定義持久化類別,需放置於Class宣告的上方。
-
table:指定持久化類別對應資料庫Table名稱。
-
@hibernate.id:定義主鍵欄位相關屬性。
-
column:指定欄位對應資料庫欄位名稱,若未指定則使用相對應的property名稱。
-
generator-class:主鍵產生器屬性,目前有uuid.hex、uuid.string、increment、assigned、native、identity、sequence、hilo、seqhilo、foreign可供設定。
-
@hibernate.property:定義持久化類別對應資料庫欄位相關屬性。
-
column:指定欄位對應資料庫欄位名稱,若未指定則使用相對應的property名稱。
-
length:指定對應資料庫欄位長度。
-
not-null:是否允許欄位存放null值。
-
unique:是否允許資料庫欄位值重覆
-
type:對應資料庫欄位型態
另外,讀者可能發現,在範例中的二個POJO都實現了java.io.Serializable介面,這是Hibernate規範中所提到的,但目前大多數資料並未說明為何必須實現Serializable。事實上Hibernate並不要求持久化類必須實現Serializable介面,但是對於採用分散式架構的Java應用,當Java物件在不同的分散節點之間傳輸時(例如:RMI),這個物件所屬的類別必須實現Serializable介面,此外,在Java Web應用中,如果希望對HttpSession中存放的Java物件進行持久化,那麼這個Java物件所屬的類也必須實現Serializable介面,因此若只拿Hibernate作單純資料庫應用可不遵守該規定外,Hibernate在J2EE的應用上都是必須實現Serializable的。
經過maven的設定與撰寫完POJO後,在Console下執行maven java:compile,當指令運作結束後,在[hibernate_example/target/classes/demo/model]路徑下將會出現User.hbm.xml與Role.hbm.xml的Hibernate Mapping file。
資料結構 1
hibernate-example
|--src
| --main
| --java
| --demo
| --model
| |--Role.java
| --User.java
|--maven.xml
|--project.properties
--project.xml
圖 1 Domain Object類別圖
程式 1 User.java
package demo.model;
import java.io.Serializable;
/**
* @hibernate.class table="app_user"
*/
public class User implements Serializable {
private Long id;
private String username;
private String password;
/**
* @hibernate.id column="id" generator-class="native"
*/
public Long getId() {
return id;
}
/**
* @hibernate.property length="50" not-null="true" unique="true"
*/
public String getUsername() {
return username;
}
/**
* @hibernate.property column="password" not-null="true"
*/
public String getPassword() {
return password;
}
…set method 省略…
}
程式 2 Role.java
package demo.model;
import java.io.Serializable;
/**
* @hibernate.class table="role"
*/
public class Role implements Serializable {
private Long id;
private String name;
private String description;
/**
* @hibernate.id column="id" generator-class="native"
*/
public Long getId() {
return id;
}
/**
* @hibernate.property column="name" length="20"
*/
public String getName() {
return name;
}
/**
* @hibernate.property column="description" length="64"
*/
public String getDescription() {
return description;
}
…set method 省略…
}
使用Hibernate Plugin建立Table Schema
到目前為止我們設定了hibernatedoclet、建立了POJO的類別並自動產生了相關的Mapping file。假設這時資料庫Schema早已建立好的話,就可以開始使用Hibernate進行DAO的開發與測試了。倘若Schema尚未建立呢?需要自行建立嗎?而自行建立則又會引發另一個問題,POJO的設計是基於Schema所建立的。當日後Schema修改時也必須對應相關的POJO或Mapping file進行維護。一次、二次還可以接受,但在專案開發的過程中需求永遠在變化,很難保證Schema不會一直變更,一旦次數一多,將引發難以重構的災難。
在Ant中可以透過net.sf.hibernate.tool.hbm2ddl.SchemaExportTask協助我們將*.hbm.xml再次轉化成相對應資料庫的Schema,而在Maven上可以使用Hibernate Plugin協助進行SchemaExport。基於某些因素Hibernate Plugin最新版本為1.3並且已有一段時間沒有新版本釋出,該版本並未支援Hibernate 3.X,若讀者使用Hibernate 2開發的話,可以直接進行下載。在本次範例中我們將下載經過1.3版本修改的Hibernate Plugin1.4 (請讀者參考相關資源4進行下載)。
將[%MAVEN_HOME%/plugins]下的1.3版移除,放置下載完成的maven-hibernate-plugin-1.4.jar於該資料夾內即完成了hibernate plugin的安裝。接著參考設定 4在project.xml中加入相依套件,此處的設定中我們加入了maven-hibernate-plugin的設定與Hibernate進行SchemaExport時所需的套件,另外為了示範Hibernate跨資料庫平台的特色,在此引入而PostgreSql與Hsqldb二個開放原始碼資料庫的JDBC協助進行測試。
在進行SchemaExport功能時可以設定是否正式進行資料庫的更新,或者只是單純的產生資料庫Schema的文字檔供開發人員進行修改或使用。為此必須正確設定Hibernate與資料庫連結的相關參數,參考設定 5與設定 6比較範例中二種資料庫設定的異同處,而關於hibernate.dialect參數的設定,請參考Hibernate官方參考手冊。
最後參考設定 7加入Hibernate plugin所需的參數,讀者對照表 2即可知相關參數的意義為何。當設定完成後於Console下執行maven hibernate:schema-export即可進行SchemaExport,在此我們分別為了PostgreSql與Hsqldb作了SchemaExport,請參考圖 2與圖 3的結果。若讀者不希望直接進行資料庫的更新,則可將project.properties檔中參數maven.hibernate.text設為yes再次進行hibernate:schema-export,即可於[target/schema/schema.sql]檔中查看匯出的資料庫SQL語法。
hibernate:schema-export這個goal可以協助我們進行建立資料庫Schema,而當進行POJO的修改並重新產生Mapping file時,可運行maven hibernate:schema-update,該指令將可在不移除資料表的清況下進行資料庫Schema的更新,對於DataBase Schema的重構有很大的幫助。
設定 4 project.xml增添相依套件
<dependency>
<groupId>maven</groupId>
<artifactId>maven-hibernate-plugin</artifactId>
<version>1.4</version>
<type>plugin</type>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.1-407.jdbc3</version>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.7.3.3</version>
</dependency>
<dependency>
<groupId>geronimo-spec</groupId>
<artifactId>geronimo-spec-jta</artifactId>
<version>1.0.1B-rc4</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.0.5</version>
</dependency>
設定 5 database-postgresql.properties
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.connection.url=jdbc:postgresql://localhost/demo
hibernate.connection.driver_class=org.postgresql.Driver
hibernate.connection.show_sql=true
hibernate.connection.username=test
hibernate.connection.password=test
設定 6 database-hsqldb.properties
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.connection.url=jdbc:hsqldb:hsql://localhost/demo
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
hibernate.connection.show_sql=true
hibernate.connection.username=sa
hibernate.connection.password=
設定 7 project.properties增添相關屬性設定
#####################################
# hibernate properties
#####################################
maven.hibernate.properties=${basedir}/database-postgresql.properties
#maven.hibernate.properties=${basedir}/database-hsqldb.properties
maven.hibernate.quiet=no
maven.hibernate.text=no
maven.hibernate.drop=no
maven.hibernate.delimiter=;
maven.hibernate.output.dir=${maven.build.dir}/schema
maven.hibernate.output.file=${maven.hibernate.output.dir}/schema.sql
表 2 maven-hibernate-plugin設定
參數 | 說明 |
maven.hibernate.properties | hibernate參數設定檔放置路徑,無論直接進行資料庫的更新或單純產出Schema的文字檔,皆必須要進行該項設定主要以hibernate.dialect參數決定使用何種資料庫方言。 |
maven.hibernate.text | 若為yes則執行Schema Export時將直接匯出SQL成文字檔,並不會進行資料庫的更新,若設為no則直接進行資料庫Schema的更新,預設為no |
maven.hibernate.quiet | 是否顯示詳細的Schema Export資訊,例:當maven.hibernate.text=no,maven.hibernate.quiet=yes時,將直接進行資料庫Schema的更新,Console下不會顯示任何訊息,預設為yes |
maven.hibernate.drop | 是否只執行drop資料表的動作,若maven.hibernate.text=yes則匯出SQL檔將只包含drop table的SQL語法。 |
maven.hibernate.delimiter | 分隔SQL Commands的定義符號,預設值為空字串,而大多數狀況使用”;”作為分隔符號。 |
maven.hibernate.output.dir | 若maven.hibernate.text=yes,匯出檔放置資料夾,預設為${maven.build.dir}/schema |
maven.hibernate.output.file | 若maven.hibernate.text=yes,匯出檔放置路徑,預設為${maven.hibernate.output.dir}/${maven.final.name}-schema.sql |
圖 2 PostgreSql SchemaExport結果
圖 3 Hsqldb SchemaExport結果
結論
Hibernate是一個開放原始碼的物件關聯映射技術,針對了JDBC進行了輕量化的將關聯資料轉化為物件封裝,使得開發人員可以隨心所欲的使用物件導向思維來駕馭資料庫。而在本期的介紹中我們了解了使用Maven協助Hibernate建立Domain Object的一種應用,而除了本次範例的方式外,Hibernate也可以使用Middlegen的方式透過現有的資料庫Schema產生出Mapping file,又或著從Mapping file建立出Schema與Java Domain Object。因此可依專案的狀況進行彈性的調整。
Hibernate的應用可以切入在任何使用JDBC的場合,既可以應用於單純的Java Application,也可以在Servlet/JSP的Web應用程式中使用,更可以應用在EJB的J2EE架構中取代CMP,完成資料持久化的重任。倘若更深入Hibernate的架構與觀念中,則將更了解物件關聯映射技術的最佳化實踐。