通過Hibernate項目中提供的幾個命令行工具(他們也被當作項目的一部分不斷得到維護),還有XDoclet,Middlegen和AndroMDA內(nèi)置的對Hibernate的支持,可以在幾個不同的環(huán)境(SQL,java代碼,xml映射文件)中進行相互轉(zhuǎn)換(roundtrip)。
Hibernate的主發(fā)行包中附帶了最重要的工具(甚至在Hibernate內(nèi)部也可以快速調(diào)用這個工具):
Hibernate項目直接提供的其他工具在一個單獨的發(fā)行包中發(fā)布,Hibernate Extensions。這個發(fā)行包包含了下列任務(wù)的工具:
-
從映射文件到Java源代碼的生成器(也就是CodeGenerator,hbm2java)
-
從已編譯的Java類或者帶有XDoclet標記的Java源代碼生成映射文件(它們是MapGenerator,class2hbm)
實際上Hibernate Extensions里面還有一個工具:ddl2hbm。但是它已經(jīng)被廢棄了,已經(jīng)不再被維護了。Middlegen完成了同樣的任務(wù),并且更加出色。
對Hibernate提供支持的第三方工具有:
這些第三方工具沒有在這篇指南中說明。請查閱Hibernate 網(wǎng)站得到關(guān)于它們目前的情況。(Hibernate主發(fā)行包中有關(guān)于整個網(wǎng)站的快照)
15.1. Schema 生成器(Schema Generation)
可以從你的映射文件使用一個命令行工具生成DDL。在Hibernate主發(fā)行包的hibernate-x.x.x/bin目錄下有一個批處理文件。
生成的schema包含有對實體和集合類表的完整性引用約束(主鍵和外鍵)。涉及到的標示符生成器所需的表和sequence也會同時生成。
在使用這個工具的時候,你必須 通過hibernate.dialet屬性指定一個SQL方言(Dialet)。
15.1.1. 對schema定制化(Customizing the schema)
很多Hibernate映射元素定義了一個可選的length屬性。你可以通過這個屬性設(shè)置字段的長度。 (如果是Or, for numeric/decimal data types, the precision.)
有些tag接受not-null屬性(用來在表字段上生成NOT NULL約束)和unique屬性(用來在表字段上生成UNIQUE約束)。
有些tag接受index屬性,用來指定字段的index名字。unique-key屬性可以對成組的字段指定一個組合鍵約束(unit key constraint)。目前,unique-key屬性指定的值并不會被當作這個約束的名字,它們只是在用來在映射文件內(nèi)部用作區(qū)分的。
示例:
<property name="foo" type="string" length="64" not-null="true"/>
<many-to-one name="bar" foreign-key="fk_foo_bar" not-null="true"/>
<element column="serial_number" type="long" not-null="true" unique="true"/>
另外,這些元素還接受<column>子元素。在定義跨越多字段的類型時特別有用。
<property name="foo" type="string">
<column name="foo" length="64" not-null="true" sql-type="text"/>
</property>
<property name="bar" type="my.customtypes.MultiColumnType"/>
<column name="fee" not-null="true" index="bar_idx"/>
<column name="fi" not-null="true" index="bar_idx"/>
<column name="fo" not-null="true" index="bar_idx"/>
</property>
sql-type屬性允許用戶覆蓋默認的Hibernate類型到SQL數(shù)據(jù)類型的映射。
check屬性允許用戶指定一個約束檢查。
<property name="foo" type="integer">
<column name="foo" check="foo > 10"/>
</property>
<class name="Foo" table="foos" check="bar < 100.0">
...
<property name="bar" type="float"/>
</class>
表 15.1. Summary
屬性(Attribute) |
值(Values) |
解釋(Interpretation) |
length |
數(shù)字 |
字段長度/小數(shù)點精度 |
not-null |
true|false |
指明字段是否應(yīng)該是非空的 |
unique |
true|false |
指明是否該字段具有惟一約束 |
index |
index_name |
指明一個(多字段)的索引(index)的名字 |
unique-key |
unique_key_name |
指明多字段惟一約束的名字(參見上面的說明) |
foreign-key |
foreign_key_name |
指明一個外鍵的名字,它是為關(guān)聯(lián)生成的。 |
sql-type |
column_type |
覆蓋默認的字段類型(只能用于<column>屬性) |
check |
SQL 表達式 |
對字段或表加入SQL約束檢查 |
SchemaExport工具把DDL腳本寫到標準輸出,同時/或者執(zhí)行DDL語句。
java -cp hibernate_classpaths net.sf.hibernate.tool.hbm2ddl.SchemaExport options mapping_files
表 15.2. SchemaExport命令行選項
選項 |
說明 |
--quiet |
不要把腳本輸出到stdout |
--drop |
只進行drop tables的步驟 |
--text |
不執(zhí)行在數(shù)據(jù)庫中運行的步驟 |
--output=my_schema.ddl |
把輸出的ddl腳本輸出到一個文件 |
--config=hibernate.cfg.xml |
從XML文件讀入Hibernate配置 |
--properties=hibernate.properties |
從文件讀入數(shù)據(jù)庫屬性 |
--format |
把腳本中的SQL語句對齊和美化 |
--delimiter=x |
為腳本設(shè)置行結(jié)束符 |
你甚至可以在你的應(yīng)用程序中嵌入SchemaExport工具:
Configuration cfg = ....;
new SchemaExport(cfg).create(false, true);
可以通過如下方式指定數(shù)據(jù)庫屬性:
所需的參數(shù)包括:
表 15.3. SchemaExport 連接屬性
屬性名 |
說明 |
hibernate.connection.driver_class |
jdbc driver class |
hibernate.connection.url |
jdbc url |
hibernate.connection.username |
database user |
hibernate.connection.password |
user password |
hibernate.dialect |
方言(dialect) |
你可以在你的Ant build腳本中調(diào)用SchemaExport:
<target name="schemaexport">
<taskdef name="schemaexport"
classname="net.sf.hibernate.tool.hbm2ddl.SchemaExportTask"
classpathref="class.path"/>
<schemaexport
properties="hibernate.properties"
quiet="no"
text="no"
drop="no"
delimiter=";"
output="schema-export.sql">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaexport>
</target>
15.1.5. 對schema的增量更新(Incremental schema updates)
SchemaUpdate工具對已存在的schema采用"增量"方式進行更新。注意SchemaUpdate嚴重依賴于JDBC metadata API,所以它并非對所有JDBC驅(qū)動都有效。
java -cp hibernate_classpaths net.sf.hibernate.tool.hbm2ddl.SchemaUpdate options mapping_files
表 15.4. SchemaUpdate命令行選項
選項 |
說明 |
--quiet |
不要把腳本輸出到stdout |
--properties=hibernate.properties |
從指定文件讀入數(shù)據(jù)庫屬性 |
你可以在你的應(yīng)用程序中嵌入SchemaUpdate工具:
Configuration cfg = ....;
new SchemaUpdate(cfg).execute(false);
15.1.6. 用Ant來增量更新schema(Using Ant for incremental schema updates)
你可以在Ant腳本中調(diào)用SchemaUpdate:
<target name="schemaupdate">
<taskdef name="schemaupdate"
classname="net.sf.hibernate.tool.hbm2ddl.SchemaUpdateTask"
classpathref="class.path"/>
<schemaupdate
properties="hibernate.properties"
quiet="no">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaupdate>
</target>
15.2. 代碼生成(Code Generation)
Hibernate代碼生成器可以用來為Hibernate映射文件生成Java實現(xiàn)類的骨架。這個工具在Hibernate Extensions發(fā)行包中提供(需要單獨下載)。
hbm2java解析映射文件,生成可工作的Java源代碼文件。使用hbm2java,你可以“只”提供.hbm文件,不用擔心要去手工編寫Java文件。
java -cp hibernate_classpaths net.sf.hibernate.tool.hbm2java.CodeGenerator options mapping_files
表 15.5. 代碼生成器命令行選項
選項 |
說明 |
--output=output_dir |
生成代碼輸出的根目錄 |
--config=config_file |
可選的hvm2java配置文件 |
配置文件提供了配置生成源代碼的多個"渲染器(renders)"的途徑,也可以聲明在全局范圍生效的<meta>屬性。詳情請參見<meta>屬性的部分。
<codegen>
<meta attribute="implements">codegen.test.IAuditable</meta>
<generate renderer="net.sf.hibernate.tool.hbm2java.BasicRenderer"/>
<generate
package="autofinders.only"
suffix="Finder"
renderer="net.sf.hibernate.tool.hbm2java.FinderRenderer"/>
</codegen>
這個配置文件聲明了一個全局的meta(元)屬性“implements”,指定了兩個渲染器,默認渲染器(BadicRender)和生成Finder(參見下面的“基本Finder 生成器”)的渲染器。
定義第二個渲染器需要一個包名和后綴屬性。
包名屬性指定生成后的源代碼應(yīng)該保存的位置,覆蓋在.hbm文件中指定的包范圍。
后綴屬性指定生成的文件的后綴。比如說,如果有一個Foo.java文件,應(yīng)該變成FooFinder.java。
也可以通過在<generate>元素上增加<param>屬性來傳遞特別的參數(shù)到渲染器去。
hbm2java目前支持一個這樣的參數(shù),名字是generate-concrete-empty-classes來通知BasicRender對你所有的類都只生成空的具體類來繼承它們。下列config.xml演示了這個功能
<codegen>
<generate prefix="Base" renderer="net.sf.hibernate.tool.hbm2java.BasicRenderer"/>
<generate renderer="net.sf.hibernate.tool.hbm2java.BasicRenderer">
<param name="generate-concrete-empty-classes">true</param>
<param name="baseclass-prefix">Base</param>
</generate>
</codegen>
注意,這個config.xml定義了兩個渲染器。一個生成Base類,第二個只生成空的具體類。
<meta>標簽時對hbm.xml文件進行的簡單注解,工具可以用這個位置來保存/閱讀和Hibernate內(nèi)核不是直接相關(guān)的一些信息。
你可以用<meta>標簽來告訴hbm2java只生成"protectd"
下面的例子:
<class name="Person">
<meta attribute="class-description">
Javadoc for the Person class
@author Frodo
</meta>
<meta attribute="implements">IAuditable</meta>
<id name="id" type="long">
<meta attribute="scope-set">protected</meta>
<generator class="increment"/>
</id>
<property name="name" type="string">
<meta attribute="field-description">The name of the person</meta>
</property>
</class>
會生成類似下面的輸出(為了有助于理解,節(jié)選部分代碼)。注意Javadoc注釋和聲明成protected的set方法:
// default package
import java.io.Serializable;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
/**
* Javadoc for the Person class
* @author Frodo
*
*/
public class Person implements Serializable, IAuditable {
/** identifier field */
public Long id;
/** nullable persistent field */
public String name;
/** full constructor */
public Person(java.lang.String name) {
this.name = name;
}
/** default constructor */
public Person() {
}
public java.lang.Long getId() {
return this.id;
}
protected void setId(java.lang.Long id) {
this.id = id;
}
/**
* The name of the person
*/
public java.lang.String getName() {
return this.name;
}
public void setName(java.lang.String name) {
this.name = name;
}
}
表 15.6. 支持的meta標簽
屬性 |
說明 |
class-description |
插入到類的javadoc說明去 |
field-description |
插入到field/property的javadoc說明去 |
interface |
如果是true,生成interface而非class |
implements |
類要實現(xiàn)的接口 |
extends |
類要繼承的超類(若是subclass,則忽略該屬性) |
generated-class |
重新指定要生成的類名 |
scope-class |
class的scope |
scope-set |
set方法的scope |
scope-get |
get方法的scope |
scope-field |
實際屬性字段(field)的scope |
use-in-tostring |
在toString()中包含此屬性 |
implement-equals |
在這個類中包含equals()和hashCode()方法 |
use-in-equals |
在equals()和hashCode() 方法中包含此屬性 |
bound |
為屬性增加propertyChangeListener支持 |
constrained |
為屬性增加vetoChangeListener支持 |
gen-property |
如果是false,不會生成屬性(謹慎使用) |
property-type |
覆蓋屬性的默認值.如果值是標簽,則指定一個具體的類型而非Object(Use this with any tag's to specify the concrete type instead of just Object.) |
class-code |
在類的最后會插入的額外代碼 |
extra-import |
在所有的import后面會插入的額外的import |
finder-method |
參見下面的"Basic finder generator" |
session-method |
參見下面的"Basic finder generator" |
通過<meta>標簽定義的屬性在一個hbm.xml文件中是默認"繼承"的。
這究竟是什么意思?如果你希望你所有的類都實現(xiàn)IAuditable接口,那么你只需要加一個<meta attribute="implements">IAuditable</meta> 在你hml.xml文件的開頭,就在<hibernate-mapping>后面。現(xiàn)在所有在hbm.xml文件中定義的類都會實現(xiàn)IAuditable了!(除了那些也特別指定了"implements"元屬性的類,因為本地指定的元標簽總是會覆蓋任何繼承的元標簽)。
注意,這條規(guī)則對所有 的<meta>標簽都有效。也就是說它可以用來指定所有的字段都被聲明成protected的,而非默認的private。這可以通過在<class>后面<meta attribute="scope-field">protected</meta>指定,那么這個類所有的field都會變成protected。
如果你不想讓<meta>標簽繼承,你可以簡單的在標簽屬性上指明inherit="false",比如<meta attribute="scope-class" inherit="false">public abstract</meta>,這樣"class-scope"就只會對當前類起作用,不會對其子類生效。
15.2.3. 基本的finder生成器(Basic finder generator)
目前可以讓hbm2java為Hibernate屬性生成基本的finder。這需要在hbm.xml文件中做兩件事情。
首先是要標記出你希望生成finder的字段。你可以通過在property標簽中的meta 塊來定義:
<property name="name" column="name" type="string">
<meta attribute="finder-method">findByName</meta>
</property>
find方法的名字就是meta標簽中間的文字。
第二件事是為hbm2java建立下面格式的配置文件:
<codegen>
<generate renderer="net.sf.hibernate.tool.hbm2java.BasicRenderer"/>
<generate suffix="Finder" renderer="net.sf.hibernate.tool.hbm2java.FinderRenderer"/>
</codegen>
然后用參數(shù)去調(diào)用:hbm2java --config=xxx.xml,xxx.xml就是你剛才創(chuàng)建的配置文件的名字。
有個可選的參數(shù),作為一個在class級別的meta標簽,格式如下:
<meta attribute="session-method">
com.whatever.SessionTable.getSessionTable().getSession();
</meta>
他是用來管理你如何使用Thread Local Session模式(在Hibernate 網(wǎng)站的Design Patterns部分有文檔)得到session的。
15.2.4. 基于Velocity的渲染器/生成器(Velocity based renderer/generator)
目前可以使用velocity作為渲染機制的一個替代方案。下面的config.xml文件顯示了如果配置hbm2java來使用velocity渲染器。
<codegen>
<generate renderer="net.sf.hibernate.tool.hbm2java.VelocityRenderer">
<param name="template">pojo.vm</param>
</generate>
</codegen>
名為template的參數(shù)是指向你希望你使用velocity macro文件的資源路徑。這個文件必須在hbm2java的classpath中。所以要記住把pojo.vm所在的路徑加入到你ant任務(wù)或者shell腳本中去。(默認的位置是./tools/src/velocity)
注意,當前的pojo.vm只生成java beans最基本的部分。他還沒有默認的渲染器那么完整,也沒有那么多功能——特別是大部分meta標簽還不支持。
15.3. 映射文件生成器(Mapping File Generation)
映射文件的骨架可以從編譯過的持久化類中使用MapGenerator工具生成。這工具是Hibernate Extensions發(fā)行包的一部分。
Hibernate映射生成器提供了從編譯過的類中產(chǎn)生映射的機制。他使用Java反射來查找屬性( properties),然后使用啟發(fā)式算法來從屬性類型猜測合適的映射。生成出來的映射文件之應(yīng)該看作是后續(xù)工作的起點。沒有辦法在沒有用戶修正的情況下生成完整的Hibernate映射。但是,這個工具還是替你做了很多非常瑣碎和麻煩的工作。
類一個一個地加入到映射去。如果工具認為某個類不是Hibernate可持久化( persistable)的,就會把這些類剔除。
判斷是否是Hibernate可持久化( persistable)的原則是:
注意,接口和內(nèi)部類實際上是可以通過Hibernate持久化的,但是一般來說用戶不會使用。
對已經(jīng)發(fā)現(xiàn)的類,MapGenerator會重復回溯到超類鏈條上去,以盡可能的把Hibernate可持久化的超類加入到對同一個數(shù)據(jù)庫表的映射去。如果回溯過程中某個類出現(xiàn)了有個屬性在下列備選UID名字(candidate UID names)名單中,回溯就會停止。
默認的備選UID屬性名有:uid, UID, id, ID, key, KEY, pk, PK。
如果類中有兩個方法,一個是setter,一個是getter,并且setter的單參數(shù)的屬性和getter的無參數(shù)返回值得類型相同,并且setter返回void,就認為發(fā)現(xiàn)了一個屬性。并且,setter的名字必須以set字符串開始,getter的名字必須以get開始,或者以is開始并且屬性類型是boolean。在上面的情況發(fā)生時,get和set之后的名字還必須匹配。這個匹配就是屬性的名字,然后如果第二個字母是小寫的話,會把其首字母變成小寫。
用來決定每個屬性的數(shù)據(jù)庫類型的規(guī)則如下:
-
如果Java類型是Hibernate.basic(),則屬性是該類型的一個普通字段。
-
對于hibernate.type.Type特定類型和PersistentEnum來說,也會使用一個普通字段。
-
如果屬性類型是一個數(shù)組,那么會使用一個Hibernate數(shù)組,并且MapGenerator試圖反映數(shù)組元素的類型。(attempts to reflect on the array element type.)
-
如果屬性是java.util.List,java.util.Map或者java.util.Set,會使用對應(yīng)的Hibernate類型,但是MapGenerator不能對這些類型進行進一步處理了。
-
如果屬性的類型不是上面任何一種,MapGeneraotr把決定數(shù)據(jù)庫類型的步驟留待所有的類都被處理之后再來做。在那時候,如果類在上面描述過的超類搜索過程中被發(fā)現(xiàn)了,這個屬性會被認為是一個many-to-one的關(guān)聯(lián)。如果類有人和屬性,它則是一個組件(component)。否則它就是可序列化的(serializable),或者不是可持久化的。
這個工具會把XML映射寫入到標準輸出或者/并且到一個文件中去。
在調(diào)用這個工具的時候,你必須把你編譯過的類放到classpath中去。
java -cp hibernate_and_your_class_classpaths net.sf.hibernate.tool.class2hbm.MapGenerator options and classnames
有兩種操作模式:命令行或者交互式。
交互式模式當你使用一個惟一的命令行參數(shù)--interact的時候啟動。這個模式提供一個命令控制臺。你可以用uid=XXX命令設(shè)置每個類的UID屬性的名字,XXX就是UID屬性名。其他可用的命令就是類名的全限定名,或者“done”命令用來輸出XML,并且結(jié)束。
在命令行模式下,下面的參數(shù)選項和所需處理的類的全限定名可以相互間隔使用。大多數(shù)選項會使用多次,每個只影響其后出現(xiàn)的類。
表 15.7. MapGenerator命令行選項
選項 |
說明 |
--quiet |
不把O-R 映射輸出到stdout |
--setUID=uid |
設(shè)置備選UID名單 |
--addUID=uid |
在備選UID名單前面增加一個新的uid |
--select=mode |
對后面的classes使用select選擇的模式(mode)(比如, distinct 或者all) |
--depth=<small-int> |
限制后面的類的組件數(shù)據(jù)遞歸層數(shù) |
--output=my_mapping.xml |
把O-R 映射輸出到一個文件 |
full.class.Name |
把這個類加入到映射中 |
--abstract=full.class.Name |
參見下面的說明 |
abstract開關(guān)指定本工具忽略特定的超類,所以它的繼承數(shù)上的類不會被映射到一個大表中去。比如,我們來看下面的類繼承樹:
Animal-->Mammal-->Human
Animal-->Mammal-->Marsupial-->Kangaroo
如果不使用--abstract開關(guān),Animal的所有子類都會被放到一個巨大的表中去,包含所有類的所有屬性,還有一個用于分辨子類的字段。如果Mammal被標記成abstract,Human和Marsupial會被映射到不同的<class>聲明,并且會有各自單獨的表。Kangaroo仍然會被認為是Marsupial的子類,除非Marsupial也標記為anstract的。