通过hibernate项目中提供的几个命令行工具(他们也被当作项目的一部分不断得到维护),还有xdoclet,middlegen和andromda内置的对hibernate的支持,可以在几个不同的环境(sql,java代码,xml映射文件)中进行相互转换(roundtrip)。
hibernate的主发行包中附带了最重要的工具(甚至在hibernate内部也可以快速调用这个工具):
-
从映射文件到ddl schema的生成器(也就是schemaexport和hbm2ddl)
hibernate项目直接提供的其他工具在一个单独的发行包中发布,hibernate extensions。这个发行包包含了下列任务的工具:
-
从映射文件到java源代码的生成器(也就是codegenerator,hbm2java)
-
从已编译的java类或者带有xdoclet标记的java源代码生成映射文件(它们是mapgenerator,class2hbm)
实际上hibernate extensions里面还有一个工具:ddl2hbm。但是它已经被废弃了,已经不再被维护了。middlegen完成了同样的任务,并且更加出色。
对hibernate提供支持的第三方工具有:
-
middlegen (从现有的数据库schema中生成映射文件)
-
andromda ( 使用mda思想(model-driven architecture ,模型驱动体系)的代码生成器,它从uml图和其xml/xmi等价形式中生成持久化类的代码)
这些第三方工具没有在这篇指南中说明。请查阅hibernate 网站得到关于它们目前的情况。(hibernate主发行包中有关于整个网站的快照)
15.1. schema 生成器(schema generation)
可以从你的映射文件使用一个命令行工具生成ddl。在hibernate主发行包的hibernate-x.x.x/bin目录下有一个批处理文件。
生成的schema包含有对实体和集合类表的完整性引用约束(主键和外键)。涉及到的标示符生成器所需的表和sequence也会同时生成。
在使用这个工具的时候,你必须 通过hibernate.dialet属性指定一个sql方言(dialet)。
15.1.1. 对schema定制化(customizing the schema)
很多hibernate映射元素定义了一个可选的length属性。你可以通过这个属性设置字段的长度。 (如果是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属性指定的值并不会被当作这个约束的名字,它们只是在用来在映射文件内部用作区分的。
示例:
<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数据类型的映射。
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 | 数字 | 字段长度/小数点精度 |
not-null | true|false | 指明字段是否应该是非空的 |
unique | true|false | 指明是否该字段具有惟一约束 |
index | index_name | 指明一个(多字段)的索引(index)的名字 |
unique-key | unique_key_name | 指明多字段惟一约束的名字(参见上面的说明) |
foreign-key | foreign_key_name | 指明一个外键的名字,它是为关联生成的。 |
sql-type | column_type | 覆盖默认的字段类型(只能用于<column>属性) |
check | sql 表达式 | 对字段或表加入sql约束检查 |
15.1.2. 运行该工具
schemaexport工具把ddl脚本写到标准输出,同时/或者执行ddl语句。
java -cp hibernate_classpaths net.sf.hibernate.tool.hbm2ddl.schemaexport options mapping_files
表 15.2. schemaexport命令行选项
选项 | 说明 |
---|---|
--quiet | 不要把脚本输出到stdout |
--drop | 只进行drop tables的步骤 |
--text | 不执行在数据库中运行的步骤 |
--output=my_schema.ddl | 把输出的ddl脚本输出到一个文件 |
--config=hibernate.cfg.xml | 从xml文件读入hibernate配置 |
--properties=hibernate.properties | 从文件读入数据库属性 |
--format | 把脚本中的sql语句对齐和美化 |
--delimiter=x | 为脚本设置行结束符 |
你甚至可以在你的应用程序中嵌入schemaexport工具:
configuration cfg = ....; new schemaexport(cfg).create(false, true);
15.1.3. 属性(properties)
可以通过如下方式指定数据库属性:
-
通过-d<property>系统参数
-
在hibernate.properties文件中
-
位于一个其它名字的properties文件中,然后用 --properties参数指定
所需的参数包括:
表 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) |
15.1.4. 使用ant(using ant)
你可以在你的ant build脚本中调用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驱动都有效。
java -cp hibernate_classpaths net.sf.hibernate.tool.hbm2ddl.schemaupdate options mapping_files
表 15.4. schemaupdate命令行选项
选项 | 说明 |
---|---|
--quiet | 不要把脚本输出到stdout |
--properties=hibernate.properties | 从指定文件读入数据库属性 |
你可以在你的应用程序中嵌入schemaupdate工具:
configuration cfg = ....; new schemaupdate(cfg).execute(false);
15.1.6. 用ant来增量更新schema(using ant for incremental schema updates)
你可以在ant脚本中调用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实现类的骨架。这个工具在hibernate extensions发行包中提供(需要单独下载)。
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配置文件 |
15.2.1. 配置文件(可选)
配置文件提供了配置生成源代码的多个"渲染器(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 生成器”)的渲染器。
定义第二个渲染器需要一个包名和后缀属性。
包名属性指定生成后的源代码应该保存的位置,覆盖在.hbm文件中指定的包范围。
后缀属性指定生成的文件的后缀。比如说,如果有一个foo.java文件,应该变成foofinder.java。
也可以通过在<generate>元素上增加<param>属性来传递特别的参数到渲染器去。
hbm2java目前支持一个这样的参数,名字是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类,第二个只生成空的具体类。
15.2.2. meta属性
<meta>标签时对hbm.xml文件进行的简单注解,工具可以用这个位置来保存/阅读和hibernate内核不是直接相关的一些信息。
你可以用<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>
会生成类似下面的输出(为了有助于理解,节选部分代码)。注意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 | 类要实现的接口 |
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文件中是默认"继承"的。
这究竟是什么意思?如果你希望你所有的类都实现iauditable接口,那么你只需要加一个<meta attribute="implements">iauditable</meta> 在你hml.xml文件的开头,就在<hibernate-mapping>后面。现在所有在hbm.xml文件中定义的类都会实现iauditable了!(除了那些也特别指定了"implements"元属性的类,因为本地指定的元标签总是会覆盖任何继承的元标签)。
注意,这条规则对所有 的<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>
然后用参数去调用:hbm2java --config=xxx.xml,xxx.xml就是你刚才创建的配置文件的名字。
有个可选的参数,作为一个在class级别的meta标签,格式如下:
<meta attribute="session-method"> com.whatever.sessiontable.getsessiontable().getsession(); </meta>
他是用来管理你如何使用thread local session模式(在hibernate 网站的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的参数是指向你希望你使用velocity macro文件的资源路径。这个文件必须在hbm2java的classpath中。所以要记住把pojo.vm所在的路径加入到你ant任务或者shell脚本中去。(默认的位置是./tools/src/velocity)
注意,当前的pojo.vm只生成java beans最基本的部分。他还没有默认的渲染器那么完整,也没有那么多功能――特别是大部分meta标签还不支持。
15.3. 映射文件生成器(mapping file generation)
映射文件的骨架可以从编译过的持久化类中使用mapgenerator工具生成。这工具是hibernate extensions发行包的一部分。
hibernate映射生成器提供了从编译过的类中产生映射的机制。他使用java反射来查找属性( properties),然后使用启发式算法来从属性类型猜测合适的映射。生成出来的映射文件之应该看作是后续工作的起点。没有办法在没有用户修正的情况下生成完整的hibernate映射。但是,这个工具还是替你做了很多非常琐碎和麻烦的工作。
类一个一个地加入到映射去。如果工具认为某个类不是hibernate可持久化( persistable)的,就会把这些类剔除。
判断是否是hibernate可持久化( persistable)的原则是:
-
必定不是一个原始类型
-
必定不是一个数组
-
必定不是一个接口
-
必定不是一个内部类
-
必定有一个默认的无参数的构造方法。
注意,接口和内部类实际上是可以通过hibernate持久化的,但是一般来说用户不会使用。
对已经发现的类,mapgenerator会重复回溯到超类链条上去,以尽可能的把hibernate可持久化的超类加入到对同一个数据库表的映射去。如果回溯过程中某个类出现了有个属性在下列备选uid名字(candidate uid names)名单中,回溯就会停止。
默认的备选uid属性名有:uid, uid, id, id, key, key, pk, pk。
如果类中有两个方法,一个是setter,一个是getter,并且setter的单参数的属性和getter的无参数返回值得类型相同,并且setter返回void,就认为发现了一个属性。并且,setter的名字必须以set字符串开始,getter的名字必须以get开始,或者以is开始并且属性类型是boolean。在上面的情况发生时,get和set之后的名字还必须匹配。这个匹配就是属性的名字,然后如果第二个字母是小写的话,会把其首字母变成小写。
用来决定每个属性的数据库类型的规则如下:
-
如果java类型是hibernate.basic(),则属性是该类型的一个普通字段。
-
对于hibernate.type.type特定类型和persistentenum来说,也会使用一个普通字段。
-
如果属性类型是一个数组,那么会使用一个hibernate数组,并且mapgenerator试图反映数组元素的类型。(attempts to reflect on the array element type.)
-
如果属性是java.util.list,java.util.map或者java.util.set,会使用对应的hibernate类型,但是mapgenerator不能对这些类型进行进一步处理了。
-
如果属性的类型不是上面任何一种,mapgeneraotr把决定数据库类型的步骤留待所有的类都被处理之后再来做。在那时候,如果类在上面描述过的超类搜索过程中被发现了,这个属性会被认为是一个many-to-one的关联。如果类有人和属性,它则是一个组件(component)。否则它就是可序列化的(serializable),或者不是可持久化的。
15.3.1. 运行此工具
这个工具会把xml映射写入到标准输出或者/并且到一个文件中去。
在调用这个工具的时候,你必须把你编译过的类放到classpath中去。
java -cp hibernate_and_your_class_classpaths net.sf.hibernate.tool.class2hbm.mapgenerator options and classnames
有两种操作模式:命令行或者交互式。
交互式模式当你使用一个惟一的命令行参数--interact的时候启动。这个模式提供一个命令控制台。你可以用uid=xxx命令设置每个类的uid属性的名字,xxx就是uid属性名。其他可用的命令就是类名的全限定名,或者“done”命令用来输出xml,并且结束。
在命令行模式下,下面的参数选项和所需处理的类的全限定名可以相互间隔使用。大多数选项会使用多次,每个只影响其后出现的类。
表 15.7. mapgenerator命令行选项
选项 | 说明 |
---|---|
--quiet | 不把o-r 映射输出到stdout |
--setuid=uid | 设置备选uid名单 |
--adduid=uid | 在备选uid名单前面增加一个新的uid |
--select=mode | 对后面的classes使用select选择的模式(mode)(比如, distinct 或者all) |
--depth=<small-int> | 限制后面的类的组件数据递归层数 |
--output=my_mapping.xml | 把o-r 映射输出到一个文件 |
full.class.name | 把这个类加入到映射中 |
--abstract=full.class.name | 参见下面的说明 |
abstract开关指定本工具忽略特定的超类,所以它的继承数上的类不会被映射到一个大表中去。比如,我们来看下面的类继承树:
animal-->mammal-->human
animal-->mammal-->marsupial-->kangaroo
如果不使用--abstract开关,animal的所有子类都会被放到一个巨大的表中去,包含所有类的所有属性,还有一个用于分辨子类的字段。如果mammal被标记成abstract,human和marsupial会被映射到不同的<class>声明,并且会有各自单独的表。kangaroo仍然会被认为是marsupial的子类,除非marsupial也标记为anstract的。