hibernate-orm/reference/zh-cn/modules/basic_mapping.xml

2704 lines
136 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<chapter id="mapping">
<title>对象/关系数据库映射基础(Basic O/R Mapping)</title>
<sect1 id="mapping-declaration" revision="1">
<title>映射定义Mapping declaration</title>
<para>
对象和关系数据库之间的映射通常是用一个XML文档(XML document)来定义的。这个映射文档被设计为易读的,
并且可以手工修改。映射语言是以Java为中心这意味着映射文档是按照持久化类的定义来创建的
而非表的定义。
</para>
<para>
请注意虽然很多Hibernate用户选择手写XML映射文档但也有一些工具可以用来生成映射文档
包括XDoclet,Middlegen和AndroMDA。
</para>
<para>
让我们从一个映射的例子开始:
</para>
<programlisting id="mapping-declaration-ex1" revision="1"><![CDATA[<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat"
table="cats"
discriminator-value="C">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="subclass"
type="character"/>
<property name="weight"/>
<property name="birthdate"
type="date"
not-null="true"
update="false"/>
<property name="color"
type="eg.types.ColorUserType"
not-null="true"
update="false"/>
<property name="sex"
not-null="true"
update="false"/>
<property name="litterId"
column="litterId"
update="false"/>
<many-to-one name="mother"
column="mother_id"
update="false"/>
<set name="kittens"
inverse="true"
order-by="litter_id">
<key column="mother_id"/>
<one-to-many class="Cat"/>
</set>
<subclass name="DomesticCat"
discriminator-value="D">
<property name="name"
type="string"/>
</subclass>
</class>
<class name="Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping>]]></programlisting>
<para>
我们现在开始讨论映射文档的内容。我们只描述Hibernate在运行时用到的文档元素和属性。
映射文档还包括一些额外的可选属性和元素它们在使用schema导出工具的时候会影响导出的数据库schema结果。
(比如,<literal> not-null</literal> 属性。)
</para>
<sect2 id="mapping-declaration-doctype" revision="2">
<title>Doctype</title>
<para>
所有的XML映射都需要定义如上所示的doctype。DTD可以从上述URL中获取
<literal>hibernate-x.x.x/src/net/sf/hibernate</literal>目录中、
<literal>hibernate.jar</literal>文件中找到。Hibernate总是会首先在它的classptah中搜索DTD文件。
如果你发现它是通过连接Internet查找DTD文件就对照你的classpath目录检查XML文件里的DTD声明。
</para>
</sect2>
<sect2 id="mapping-declaration-mapping" revision="3">
<title>hibernate-mapping</title>
<para>
这个元素包括一些可选的属性。<literal>schema</literal><literal>catalog</literal>属性,
指明了这个映射所连接refer的表所在的schema和/或catalog名称。
假若指定了这个属性表名会加上所指定的schema和catalog的名字扩展为全限定名。假若没有指定表名就不会使用全限定名。
<literal>default-cascade</literal>指定了未明确注明<literal>cascade</literal>属性的Java属性和
集合类Hibernate会采取什么样的默认级联风格。<literal>auto-import</literal>属性默认让我们在查询语言中可以使用
非全限定名的类名。
</para>
<programlistingco>
<areaspec>
<area id="hm1" coords="2 55"/>
<area id="hm2" coords="3 55"/>
<area id="hm3" coords="4 55"/>
<area id="hm4" coords="5 55"/>
<area id="hm5" coords="6 55"/>
<area id="hm6" coords="7 55"/>
<area id="hm7" coords="8 55"/>
</areaspec>
<programlisting><![CDATA[<hibernate-mapping
schema="schemaName"
catalog="catalogName"
default-cascade="cascade_style"
default-access="field|property|ClassName"
default-lazy="true|false"
auto-import="true|false"
package="package.name"
/>]]></programlisting>
<calloutlist>
<callout arearefs="hm1">
<para>
<literal>schema</literal> (可选): 数据库schema的名称。
</para>
</callout>
<callout arearefs="hm2">
<para>
<literal>catalog</literal> (可选): 数据库catalog的名称。
</para>
</callout>
<callout arearefs="hm3">
<para>
<literal>default-cascade</literal> (可选 - 默认为 <literal>none</literal>):
默认的级联风格。
</para>
</callout>
<callout arearefs="hm4">
<para>
<literal>default-access</literal> (可选 - 默认为 <literal>property</literal>):
Hibernate用来访问属性的策略。可以通过实现<literal>PropertyAccessor</literal>接口
自定义。
</para>
</callout>
<callout arearefs="hm5">
<para>
<literal>default-lazy</literal> (可选 - 默认为 <literal>true</literal>):
指定了未明确注明<literal>lazy</literal>属性的Java属性和集合类
Hibernate会采取什么样的默认加载风格。
</para>
</callout>
<callout arearefs="hm6">
<para>
<literal>auto-import</literal> (可选 - 默认为 <literal>true</literal>):
指定我们是否可以在查询语言中使用非全限定的类名(仅限于本映射文件中的类)。
</para>
</callout>
<callout arearefs="hm7">
<para>
<literal>package</literal> (可选): 指定一个包前缀,如果在映射文档中没有指定全限定的类名,
就使用这个作为包名。
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
假若你有两个持久化类,它们的非全限定名是一样的(就是两个类的名字一样,所在的包不一样--译者注),
你应该设置<literal>auto-import="false"</literal>。假若说你把一个“import过”的名字同时对应两个类
Hibernate会抛出一个异常。
</para>
<para>
注意<literal>hibernate-mapping</literal> 元素允许你嵌套多个如上所示的
<literal>&lt;class&gt;</literal>映射。但是最好的做法(也许一些工具需要的)是一个
持久化类(或一个类的继承层次)对应一个映射文件,并以持久化的超类名称命名,例如:
<literal>Cat.hbm.xml</literal>
<literal>Dog.hbm.xml</literal>,或者如果使用继承,<literal>Animal.hbm.xml</literal>
</para>
</sect2>
<sect2 id="mapping-declaration-class" revision="3">
<title>class</title>
<para>
你可以使用<literal>class</literal>元素来定义一个持久化类:
</para>
<programlistingco>
<areaspec>
<area id="class1" coords="2 55"/>
<area id="class2" coords="3 55" />
<area id="class3" coords="4 55"/>
<area id="class4" coords="5 55" />
<area id="class5" coords="6 55"/>
<area id="class6" coords="7 55" />
<area id="class7" coords="8 55"/>
<area id="class8" coords="9 55" />
<area id="class9" coords="10 55" />
<area id="class10" coords="11 55"/>
<area id="class11" coords="12 55"/>
<area id="class12" coords="13 55"/>
<area id="class13" coords="14 55"/>
<area id="class14" coords="15 55"/>
<area id="class15" coords="16 55"/>
<area id="class16" coords="17 55"/>
<area id="class17" coords="18 55"/>
<area id="class18" coords="19 55"/>
<area id="class19" coords="20 55"/>
<area id="class20" coords="21 55"/>
<area id="class21" coords="22 55"/>
<area id="class22" coords="23 55"/>
<area id="class23" coords="24 55"/>
</areaspec>
<programlisting><![CDATA[<class
name="ClassName"
table="tableName"
discriminator-value="discriminator_value"
mutable="true|false"
schema="owner"
catalog="catalog"
proxy="ProxyInterface"
dynamic-update="true|false"
dynamic-insert="true|false"
select-before-update="true|false"
polymorphism="implicit|explicit"
where="arbitrary sql where condition"
persister="PersisterClass"
batch-size="N"
optimistic-lock="none|version|dirty|all"
lazy="true|false"
entity-name="EntityName"
check="arbitrary sql check condition"
rowid="rowid"
subselect="SQL expression"
abstract="true|false"
entity-name="EntityName"
node="element-name"
/>]]></programlisting>
<calloutlist>
<callout arearefs="class1">
<para>
<literal>name</literal> (可选): 持久化类或者接口的Java全限定名。
如果这个属性不存在Hibernate将假定这是一个非POJO的实体映射。
</para>
</callout>
<callout arearefs="class2">
<para>
<literal>table</literal> (可选 - 默认是类的非全限定名): 对应的数据库表名。
</para>
</callout>
<callout arearefs="class3">
<para>
<literal>discriminator-value</literal> (可选 - 默认和类名一样):
一个用于区分不同的子类的值,在多态行为时使用。它可以接受的值包括
<literal>null</literal><literal>not null</literal>
</para>
</callout>
<callout arearefs="class4">
<para>
<literal>mutable</literal> (可选,默认值为<literal>true</literal>):
表明该类的实例是可变的或者可变的。
</para>
</callout>
<callout arearefs="class5">
<para>
<literal>schema</literal> (可选):
覆盖在根<literal>&lt;hibernate-mapping&gt;</literal>元素中指定的schema名字。
</para>
</callout>
<callout arearefs="class6">
<para>
<literal>catalog</literal> (可选):
覆盖在根<literal>&lt;hibernate-mapping&gt;</literal>元素中指定的catalog名字。
</para>
</callout>
<callout arearefs="class7">
<para>
<literal>proxy</literal> (可选): 指定一个接口,在延迟装载时作为代理使用。
你可以在这里使用该类自己的名字。
</para>
</callout>
<callout arearefs="class8">
<para>
<literal>dynamic-update</literal> (可选, 默认为 <literal>false</literal>):
指定用于<literal>UPDATE</literal> 的SQL将会在运行时动态生成并且只更新那些改变过的字段。
</para>
</callout>
<callout arearefs="class9">
<para>
<literal>dynamic-insert</literal> (可选, 默认为 <literal>false</literal>):
指定用于<literal>INSERT</literal>的 SQL 将会在运行时动态生成,并且只包含那些非空值字段。
</para>
</callout>
<callout arearefs="class10">
<para>
<literal>select-before-update</literal> (可选, 默认为 <literal>false</literal>):
指定Hibernate除非确定对象真正被修改了如果该值为true译注否则<emphasis>不会</emphasis>执行SQL
<literal>UPDATE</literal>操作。在特定场合实际上它只在一个瞬时对象transient object关联到一个
新的session中时执行的update()中生效这说明Hibernate会在<literal>UPDATE</literal>
之前执行一次额外的SQL <literal>SELECT</literal>操作,来决定是否应该执行
<literal>UPDATE</literal>
</para>
</callout>
<callout arearefs="class11">
<para>
<literal>polymorphism多态</literal> (可选, 默认值为 <literal>implicit (隐式)
</literal>): 界定是隐式还是显式的使用多态查询这只在Hibernate的具体表继承策略中用到译注
</para>
</callout>
<callout arearefs="class12">
<para>
<literal>where</literal> (可选) 指定一个附加的SQL<literal>WHERE</literal> 条件,
在抓取这个类的对象时会一直增加这个条件。
</para>
</callout>
<callout arearefs="class13">
<para>
<literal>persister</literal> (可选): 指定一个定制的<literal>ClassPersister</literal>
</para>
</callout>
<callout arearefs="class14">
<para>
<literal>batch-size</literal> (可选,默认是<literal>1</literal>) 指定一个用于
根据标识符identifier抓取实例时使用的"batch size"(批次抓取数量)。
</para>
</callout>
<callout arearefs="class15">
<para>
<literal>optimistic-lock乐观锁定</literal>
(可选,默认是<literal>version</literal>): 决定乐观锁定的策略。
</para>
</callout>
<callout arearefs="class16">
<para>
<literal>lazy</literal> (optional): 通过设置<literal>lazy="false"</literal>
所有的延迟加载Lazy fetching功能将未被激活disabled
</para>
</callout>
<callout arearefs="class17">
<para>
<literal>entity-name</literal> (可选): Hibernate3允许一个类进行多次映射
默认情况是映射到不同的表并且允许使用Maps或XML代替Java层次的实体映射
(也就是实现动态领域模型,不用写持久化类-译注)。
更多信息请看<xref linkend="persistent-classes-dynamicmodels"/> and <xref linkend="xml"/>
</para>
</callout>
<callout arearefs="class18">
<para>
<literal>check</literal> (可选): 这是一个SQL表达式
用于为自动生成的schema添加多行multi-row约束<emphasis>检查</emphasis>
</para>
</callout>
<callout arearefs="class19">
<para>
<literal>rowid</literal> (可选): Hibernate可以使用数据库支持的所谓的ROWIDs例如
Oracle数据库如果你设置这个可选的<literal>rowid</literal>
Hibernate可以使用额外的字段<literal>rowid</literal>实现快速更新。ROWID是这个功能实现的重点
它代表了一个存储元组tuple的物理位置。
</para>
</callout>
<callout arearefs="class20">
<para>
<literal>subselect</literal> (可选): 它将一个不可变immutable并且只读的实体映射到一个数据库的
子查询中。它用于实现一个视图代替一张基本表,但是最好不要这样做。更多的介绍请看下面内容。
</para>
</callout>
<callout arearefs="class21">
<para>
<literal>abstract</literal> (可选): 用于在<literal>&lt;union-subclass&gt;</literal>的继承结构
hierarchies中标识抽象超类。
</para>
</callout>
<callout arearefs="class22">
<para>
<literal>entity-name</literal> (可选, 默认为类名): 显式指定实体名
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
若指明的持久化类实际上是一个接口,这也是完全可以接受的。
之后你可以用<literal>&lt;subclass&gt;</literal>来指定该接口的实际实现类。
你可以持久化任何<emphasis>static</emphasis>(静态的)内部类。
你应该使用标准的类名格式来指定类名,<literal>比如Foo$Bar</literal>
</para>
<para>
不可变类,<literal>mutable="false"</literal>不可以被应用程序更新或者删除。
这可以让Hibernate做一些小小的性能优化。
</para>
<para>
可选的<literal>proxy</literal>属性允许延迟加载类的持久化实例。
Hibernate开始会返回实现了这个命名接口的CGLIB代理。当代理的某个方法被实际调用的时候
真实的持久化对象才会被装载。参见下面的“用于延迟装载的代理”。
</para>
<para><emphasis>Implicit</emphasis> (隐式)的多态是指,如果查询时给出的是任何超类、该类实现的接口或者该类的
名字,都会返回这个类的实例;如果查询中给出的是子类的名字,则会返回子类的实例。
<emphasis>Explicit</emphasis> (显式)的多态是指,只有在查询时给出明确的该类名字时才会返回这个类的实例;
同时只有在这个<literal>&lt;class&gt;</literal>的定义中作为<literal>&lt;subclass&gt;</literal>
或者<literal>&lt;joined-subclass&gt;</literal>出现的子类,才会可能返回。
在大多数情况下,默认的<literal>polymorphism="implicit"</literal>都是合适的。
显式的多态在有两个不同的类映射到同一个表的时候很有用。(允许一个“轻型”的类,只包含部分表字段)。
</para>
<para>
<literal>persister</literal>属性可以让你定制这个类使用的持久化策略。
你可以指定你自己实现
<literal>org.hibernate.persister.EntityPersister</literal>的子类,你甚至可以完全从头开始编写一个
<literal>org.hibernate.persister.ClassPersister</literal>接口的实现,
比如是用储存过程调用、序列化到文件或者LDAP数据库来实现。
参阅<literal>org.hibernate.test.CustomPersister</literal>,这是一个简单的例子
(“持久化”到一个<literal>Hashtable</literal>)。
</para>
<para>
请注意<literal>dynamic-update</literal><literal>dynamic-insert</literal>的设置并不会继承到子类,
所以在<literal>&lt;subclass&gt;</literal>或者<literal>&lt;joined-subclass&gt;</literal>元素中可能
需要再次设置。这些设置是否能够提高效率要视情形而定。请用你的智慧决定是否使用。
</para>
<para>
使用<literal>select-before-update</literal>通常会降低性能。如果你重新连接一个脱管detache对象实例
到一个<literal>Session</literal>中时它可以防止数据库不必要的触发update。
这就很有用了。
</para>
<para>
如果你打开了<literal>dynamic-update</literal>,你可以选择几种乐观锁定的策略:
</para>
<itemizedlist>
<listitem>
<para>
<literal>version版本检查</literal> 检查version/timestamp字段
</para>
</listitem>
<listitem>
<para>
<literal>all全部</literal> 检查全部字段
</para>
</listitem>
<listitem>
<para>
<literal>dirty脏检查</literal>只检察修改过的字段
</para>
</listitem>
<listitem>
<para>
<literal>none不检查</literal>不使用乐观锁定
</para>
</listitem>
</itemizedlist>
<para>
我们<emphasis>非常</emphasis>强烈建议你在Hibernate中使用version/timestamp字段来进行乐观锁定。
对性能来说这是最好的选择并且这也是唯一能够处理在session外进行操作的策略例如
在使用<literal>Session.merge()</literal>的时候)。
</para>
<para>
对Hibernate映射来说视图和表是没有区别的这是因为它们在数据层都是透明的
注意:一些数据库不支持视图属性,特别是更新的时候)。有时你想使用视图,但却不能在数据库
中创建它例如在遗留的schema中。这样的话你可以映射一个不可变的immutable并且是
只读的实体到一个给定的SQL子查询表达式
</para>
<programlisting><![CDATA[<class name="Summary">
<subselect>
select item.name, max(bid.amount), count(*)
from item
join bid on bid.item_id = item.id
group by item.name
</subselect>
<synchronize table="item"/>
<synchronize table="bid"/>
<id name="name"/>
...
</class>]]></programlisting>
<para>
定义这个实体用到的表为同步synchronize确保自动刷新auto-flush正确执行
并且依赖原实体的查询不会返回过期数据。<literal>&lt;subselect&gt;</literal>在属性元素
和一个嵌套映射元素中都可见。
</para>
</sect2>
<sect2 id="mapping-declaration-id" revision="3">
<title>id</title>
<para>
被映射的类<emphasis>必须</emphasis>定义对应数据库表主键字段。大多数类有一个JavaBeans风格的属性
为每一个实例包含唯一的标识。<literal>&lt;id&gt;</literal> 元素定义了该属性到数据库表主键字段的映射。
</para>
<programlistingco>
<areaspec>
<area id="id1" coords="2 70"/>
<area id="id2" coords="3 70" />
<area id="id3" coords="4 70"/>
<area id="id4" coords="5 70" />
<area id="id5" coords="6 70" />
</areaspec>
<programlisting><![CDATA[<id
name="propertyName"
type="typename"
column="column_name"
unsaved-value="null|any|none|undefined|id_value"
access="field|property|ClassName"
node="element-name|@attribute-name|element/@attribute|.">
<generator class="generatorClass"/>
</id>]]></programlisting>
<calloutlist>
<callout arearefs="id1">
<para>
<literal>name</literal> (可选): 标识属性的名字。
</para>
</callout>
<callout arearefs="id2">
<para>
<literal>type</literal> (可选): 标识Hibernate类型的名字。
</para>
</callout>
<callout arearefs="id3">
<para>
<literal>column</literal> (可选 - 默认为属性名): 主键字段的名字。
</para>
</callout>
<callout arearefs="id4">
<para>
<literal>unsaved-value</literal> (可选 - 默认为一个字段判断sensible的值):
一个特定的标识属性值,用来标志该实例是刚刚创建的,尚未保存。
这可以把这种实例和从以前的session中装载过可能又做过修改--译者注)
但未再次持久化的实例区分开来。
</para>
</callout>
<callout arearefs="id5">
<para>
<literal>access</literal> (可选 - 默认为<literal>property</literal>):
Hibernate用来访问属性值的策略。
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
如果 <literal>name</literal>属性不存在,会认为这个类没有标识属性。
</para>
<para>
<literal>unsaved-value</literal> 属性很重要!如果你的类的标识属性不是默认为
正常的Java默认值null或零你应该指定正确的默认值。
</para>
<para>
还有一个另外的<literal>&lt;composite-id&gt;</literal>定义可以访问旧式的多主键数据。
我们强烈不建议使用这种方式。
</para>
<sect3 id="mapping-declaration-id-generator" revision="2">
<title>Generator</title>
<para>
可选的<literal>&lt;generator&gt;</literal>子元素是一个Java类的名字
用来为该持久化类的实例生成唯一的标识。如果这个生成器实例需要某些配置值或者初始化参数,
<literal>&lt;param&gt;</literal>元素来传递。
</para>
<programlisting><![CDATA[<id name="id" type="long" column="cat_id">
<generator class="org.hibernate.id.TableHiLoGenerator">
<param name="table">uid_table</param>
<param name="column">next_hi_value_column</param>
</generator>
</id>]]></programlisting>
<para>
所有的生成器都实现<literal>org.hibernate.id.IdentifierGenerator</literal>接口。
这是一个非常简单的接口;某些应用程序可以选择提供他们自己特定的实现。当然,
Hibernate提供了很多内置的实现。下面是一些内置生成器的快捷名字
<variablelist>
<varlistentry>
<term><literal>increment</literal></term>
<listitem>
<para>
用于为<literal>long</literal>, <literal>short</literal>或者<literal>int</literal>类型生成
唯一标识。只有在没有其他进程往同一张表中插入数据时才能使用。
<emphasis>在集群下不要使用。</emphasis>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>identity</literal></term>
<listitem>
<para>
对DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的内置标识字段提供支持。
返回的标识符是<literal>long</literal>, <literal>short</literal> 或者<literal>int</literal>类型的。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>sequence</literal></term>
<listitem>
<para>
在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列sequence)
而在Interbase中使用生成器(generator)。返回的标识符是<literal>long</literal>,
<literal>short</literal>或者 <literal>int</literal>类型的。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>hilo</literal></term>
<listitem>
<para id="mapping-declaration-id-hilodescription" revision="1">
使用一个高/低位算法高效的生成<literal>long</literal>, <literal>short</literal>
或者 <literal>int</literal>类型的标识符。给定一个表和字段(默认分别是是
<literal>hibernate_unique_key</literal><literal>next_hi</literal>)作为高位值的来源。
高/低位算法生成的标识符只在一个特定的数据库中是唯一的。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>seqhilo</literal></term>
<listitem>
<para>
使用一个高/低位算法来高效的生成<literal>long</literal>, <literal>short</literal>
或者 <literal>int</literal>类型的标识符给定一个数据库序列sequence)的名字。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>uuid</literal></term>
<listitem>
<para>
用一个128-bit的UUID算法生成字符串类型的标识符
这在一个网络中是唯一的使用了IP地址。UUID被编码为一个32位16进制数字的字符串。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>guid</literal></term>
<listitem>
<para>
在MS SQL Server 和 MySQL 中使用数据库生成的GUID字符串。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>native</literal></term>
<listitem>
<para>
根据底层数据库的能力选择<literal>identity</literal>, <literal>sequence</literal>
或者<literal>hilo</literal>中的一个。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>assigned</literal></term>
<listitem>
<para>
让应用程序在<literal>save()</literal>之前为对象分配一个标示符。这是
<literal>&lt;generator&gt;</literal>元素没有指定时的默认生成策略。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>select</literal></term>
<listitem>
<para>
通过数据库触发器选择一些唯一主键的行并返回主键值来分配一个主键。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>foreign</literal></term>
<listitem>
<para>
使用另外一个相关联的对象的标识符。通常和<literal>&lt;one-to-one&gt;</literal>联合起来使用。
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</sect3>
<sect3 id="mapping-declaration-id-hilo" revision="1">
<title>高/低位算法Hi/Lo Algorithm</title>
<para>
<literal>hilo</literal><literal>seqhilo</literal>生成器给出了两种hi/lo算法的实现
这是一种很令人满意的标识符生成算法。第一种实现需要一个“特殊”的数据库表来保存下一个可用的“hi”值。
第二种实现使用一个Oracle风格的序列在被支持的情况下
</para>
<programlisting><![CDATA[<id name="id" type="long" column="cat_id">
<generator class="hilo">
<param name="table">hi_value</param>
<param name="column">next_value</param>
<param name="max_lo">100</param>
</generator>
</id>]]></programlisting>
<programlisting><![CDATA[<id name="id" type="long" column="cat_id">
<generator class="seqhilo">
<param name="sequence">hi_value</param>
<param name="max_lo">100</param>
</generator>
</id>]]></programlisting>
<para>
很不幸你在为Hibernate自行提供<literal>Connection</literal>时无法使用<literal>hilo</literal>
当Hibernate使用JTA获取应用服务器的数据源连接时,你必须正确地配置
<literal>hibernate.transaction.manager_lookup_class</literal>
</para>
</sect3>
<sect3 id="mapping-declaration-id-uuid">
<title>UUID算法UUID Algorithm </title>
<para>
UUID包含IP地址JVM的启动时间精确到1/4秒系统时间和一个计数器值在JVM中唯一
在Java代码中不可能获得MAC地址或者内存地址所以这已经是我们在不使用JNI的前提下的能做的最好实现了。
</para>
</sect3>
<sect3 id="mapping-declaration-id-sequences">
<title>标识字段和序列Identity columns and Sequences</title>
<para>
对于内部支持标识字段的数据库(DB2,MySQL,Sybase,MS SQL),你可以使用<literal>identity</literal>关键字生成。
对于内部支持序列的数据库DB2,Oracle, PostgreSQL, Interbase, McKoi,SAP DB),
你可以使用<literal>sequence</literal>风格的关键字生成。
这两种方式对于插入一个新的对象都需要两次SQL查询。
</para>
<programlisting><![CDATA[<id name="id" type="long" column="person_id">
<generator class="sequence">
<param name="sequence">person_id_sequence</param>
</generator>
</id>]]></programlisting>
<programlisting><![CDATA[<id name="id" type="long" column="person_id" unsaved-value="0">
<generator class="identity"/>
</id>]]></programlisting>
<para>
对于跨平台开发,<literal>native</literal>策略会从<literal>identity</literal>,
<literal>sequence</literal><literal>hilo</literal>中进行选择,选择哪一个,这取决于底层数据库的支持能力。
</para>
</sect3>
<sect3 id="mapping-declaration-id-assigned">
<title>程序分配的标识符Assigned Identifiers</title>
<para>
如果你需要应用程序分配一个标示符而非Hibernate来生成你可以使用<literal>assigned</literal>
生成器。这种特殊的生成器会使用已经分配给对象的标识符属性的标识符值。
这个生成器使用一个自然键natural key有商业意义的列译注作为主键而不是使用一个代理键
surrogate key没有商业意义的列译注
</para>
<para>
当选择<literal>assigned</literal>生成器时除非有一个version或timestamp属性或者你定义了
<literal>Interceptor.isUnsaved()</literal>否则需要让Hiberante使用
<literal>unsaved-value="undefined"</literal>强制Hibernatet查询数据库来确定一个实例是瞬时的transient
还是脱管的detached
</para>
</sect3>
<sect3 id="mapping-declaration-id-select">
<title>触发器实现的主键生成器Primary keys assigned by triggers</title>
<para>
仅仅用于遗留的schema中 (Hibernate不能使用触发器生成DDL)。
</para>
<programlisting><![CDATA[<id name="id" type="long" column="person_id">
<generator class="select">
<param name="key">socialSecurityNumber</param>
</generator>
</id>]]></programlisting>
<para>
在上面的例子中,类定义了一个命名为<literal>socialSecurityNumber</literal>的唯一值属性,
它是一个自然键natural key命名为<literal>person_id</literal>的代理键surrogate key
的值由触发器生成。
</para>
</sect3>
</sect2>
<sect2 id="mapping-declaration-compositeid" revision="2">
<title>composite-id</title>
<programlisting><![CDATA[<composite-id
name="propertyName"
class="ClassName"
unsaved-value="undefined|any|none"
access="field|property|ClassName"
node="element-name|."
>
<key-property name="propertyName" type="typename" column="column_name"/>
<key-many-to-one name="propertyName class="ClassName" column="column_name"/>
......
</composite-id>]]></programlisting>
<para>
For a table with a composite key, you may map multiple properties of the class
as identifier properties. The <literal>&lt;composite-id&gt;</literal> element
accepts <literal>&lt;key-property&gt;</literal> property mappings and
<literal>&lt;key-many-to-one&gt;</literal> mappings as child elements.
</para>
<para>
如果表使用联合主键,你可以映射类的多个属性为标识符属性。
<literal>&lt;composite-id&gt;</literal>元素接受<literal>&lt;key-property&gt;</literal>
属性映射和<literal>&lt;key-many-to-one&gt;</literal>属性映射作为子元素。
</para>
<programlisting><![CDATA[<composite-id>
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id>]]></programlisting>
<para>
你的持久化类<emphasis>必须</emphasis>重载<literal>equals()</literal>
<literal>hashCode()</literal>方法,来实现组合的标识符的相等判断。
实现<literal>Serializable</literal>接口也是必须的。
</para>
<para>
不幸的是,这种组合关键字的方法意味着一个持久化类是它自己的标识。除了对象自己之外,
没有什么方便的“把手”可用。你必须自己初始化持久化类的实例,在使用组合关键字<literal>load()</literal>
持久化状态之前,必须填充他的联合属性。我们会在<xref linkend="components-compositeid"/>章中说明一种
更加便捷的方法,把联合标识实现为一个独立的类,下面描述的属性只对这种备用方法有效:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>name</literal> (可选):一个组件类型,持有复合标识(参见下一节)。
</para>
</listitem>
<listitem>
<para>
<literal>class</literal> (可选 - 默认为通过反射(reflection)得到的属性类型) :
作为联合标识的组件类名(参见下一节)。
</para>
</listitem>
<listitem>
<para>
<literal>unsaved-value</literal> (可选 - 默认为 <literal>undefined</literal>):
如果设置为<literal>any</literal>就表示瞬时transient实例应该被重新初始化或者如果
设置为<literal>none</literal>,则表示该实例是脱管对象。最好在所有的情况下都保持默认的值。
</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="mapping-declaration-discriminator" revision="3">
<title>鉴别器discriminator</title>
<para>
在"一棵对象继承树对应一个表"的策略中,<literal>&lt;discriminator&gt;</literal>元素是必需的,
它定义了表的鉴别器字段。鉴别器字段包含标志值,用于告知持久化层应该为某个特定的行创建哪一个子类的实例。
如下这些受到限制的类型可以使用:
<literal>string</literal>, <literal>character</literal>, <literal>integer</literal>,
<literal>byte</literal>, <literal>short</literal>, <literal>boolean</literal>,
<literal>yes_no</literal>, <literal>true_false</literal>.
</para>
<programlistingco>
<areaspec>
<area id="discriminator1" coords="2 60"/>
<area id="discriminator2" coords="3 60" />
<area id="discriminator3" coords="4 60" />
<area id="discriminator4" coords="5 60" />
<area id="discriminator5" coords="6 60" />
</areaspec>
<programlisting><![CDATA[<discriminator
column="discriminator_column"
type="discriminator_type"
force="true|false"
insert="true|false"
formula="arbitrary sql expression"
/>]]></programlisting>
<calloutlist>
<callout arearefs="discriminator1">
<para>
<literal>column</literal> (可选 - 默认为 <literal>class</literal>) 鉴别器字段的名字
</para>
</callout>
<callout arearefs="discriminator2">
<para>
<literal>type</literal> (可选 - 默认为 <literal>string</literal>) 一个Hibernate字段类型的名字
</para>
</callout>
<callout arearefs="discriminator3">
<para>
<literal>force(强制)</literal> (可选 - 默认为 <literal>false</literal>)
"强制"Hibernate指定允许的鉴别器值,就算取得的所有实例都是根类的。
</para>
</callout>
<callout arearefs="discriminator4">
<para>
<literal>insert</literal> (可选 - 默认为<literal>true</literal>)
如果你的鉴别器字段也是映射为复合标识composite identifier的一部分则需将
这个值设为<literal>false</literal>告诉Hibernate在做SQL <literal>INSERT</literal>
时不包含该列)
</para>
</callout>
<callout arearefs="discriminator5">
<para>
<literal>formula</literal> (可选)
一个SQL表达式在类型判断判断是父类还是具体子类译注时执行。可用于基于内容的鉴别器。
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
鉴别器字段的实际值是根据<literal>&lt;class&gt;</literal><literal>&lt;subclass&gt;</literal>元素中
<literal>discriminator-value</literal>属性得来的。
</para>
<para>
<literal>force</literal>属性仅仅是在表包含一些未指定应该映射到哪个持久化类的时候才是有用的。
这种情况不会经常遇到。
</para>
<para>
使用<literal>formula</literal>属性你可以定义一个SQL表达式用来判断一个行数据的类型。
</para>
<programlisting><![CDATA[<discriminator
formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
type="integer"/>]]></programlisting>
</sect2>
<sect2 id="mapping-declaration-version" revision="1">
<title>版本version(可选)</title>
<para>
<literal>&lt;version&gt;</literal>元素是可选的,表明表中包含附带版本信息的数据。
这在你准备使用<emphasis> 长事务long transactions</emphasis>的时候特别有用。(见后)
</para>
<programlistingco>
<areaspec>
<area id="version1" coords="2 70"/>
<area id="version2" coords="3 70"/>
<area id="version3" coords="4 70"/>
<area id="version4" coords="5 70"/>
<area id="version5" coords="6 70"/>
</areaspec>
<programlisting><![CDATA[<version
column="version_column"
name="propertyName"
type="typename"
access="field|property|ClassName"
unsaved-value="null|negative|undefined"
node="element-name|@attribute-name|element/@attribute|."
/>]]></programlisting>
<calloutlist>
<callout arearefs="version1">
<para>
<literal>column</literal> (可选 - 默认为属性名): 指定持有版本号的字段名。
</para>
</callout>
<callout arearefs="version2">
<para>
<literal>name</literal>: 持久化类的属性名。
</para>
</callout>
<callout arearefs="version3">
<para>
<literal>type</literal> (可选 - 默认是 <literal>integer</literal>): 版本号的类型。
</para>
</callout>
<callout arearefs="version4">
<para>
<literal>access</literal> (可选 - 默认是 <literal>property</literal>):
Hibernate用于访问属性值的策略。
</para>
</callout>
<callout arearefs="version5">
<para>
<literal>unsaved-value</literal> (可选 - 默认是<literal>undefined</literal>):
用于标明某个实例时刚刚被实例化的(尚未保存)版本属性值,依靠这个值就可以把这种情况
和已经在先前的session中保存或装载的脱管detached实例区分开来。
<literal>undefined</literal>指明使用标识属性值进行判断。)
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
版本号必须是以下类型:<literal>long</literal>, <literal>integer</literal>,
<literal>short</literal>, <literal>timestamp</literal>或者<literal>calendar</literal>
</para>
<para>
一个脱管detached实例的version或timestamp不能为空null因为Hibernate不管
<literal>unsaved-value</literal>指定为何种策略它将分离任何属性为空的version或timestamp
实例为瞬时transient实例。
<emphasis>避免Hibernate中的传递重附transitive reattachment问题的一个简单方法是
定义一个不能为空的version或timestamp属性特别是在人们使用程序分配的标识符assigned identifiers
或复合主键时非常有用!</emphasis>
</para>
</sect2>
<sect2 id="mapping-declaration-timestamp">
<title>timestamp (optional)</title>
<para>
可选的<literal>&lt;timestamp&gt;</literal>元素指明了表中包含时间戳数据。
这用来作为版本的替代。时间戳本质上是一种对乐观锁定的一种不是特别安全的实现。当然,
有时候应用程序可能在其他方面使用时间戳。
</para>
<programlistingco>
<areaspec>
<area id="timestamp1" coords="2 70"/>
<area id="timestamp2" coords="3 70" />
<area id="timestamp3" coords="4 70" />
<area id="timestamp4" coords="5 70" />
</areaspec>
<programlisting><![CDATA[<timestamp
column="timestamp_column"
name="propertyName"
access="field|property|ClassName"
unsaved-value="null|undefined"
node="element-name|@attribute-name|element/@attribute|."
/>]]></programlisting>
<calloutlist>
<callout arearefs="timestamp1">
<para>
<literal>column</literal> (可选 - 默认为属性名): 持有时间戳的字段名。
</para>
</callout>
<callout arearefs="timestamp2">
<para>
<literal>name</literal>: 在持久化类中的JavaBeans风格的属性名
其Java类型是 <literal>Date</literal> 或者 <literal>Timestamp</literal>的。
</para>
</callout>
<callout arearefs="timestamp3">
<para>
<literal>access</literal> (可选 - 默认是 <literal>property</literal>):
Hibernate用于访问属性值的策略。
</para>
</callout>
<callout arearefs="timestamp4">
<para>
<literal>unsaved-value</literal> (可选 - 默认是<literal>null</literal>):
用于标明某个实例时刚刚被实例化的(尚未保存)版本属性值,依靠这个值就可以把这种情况和
已经在先前的session中保存或装载的脱管detached实例区分开来。<literal>undefined</literal>
指明使用标识属性值进行这种判断。)
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
注意,<literal>&lt;timestamp&gt;</literal><literal>&lt;version type="timestamp"&gt;</literal>是等价的。
</para>
</sect2>
<sect2 id="mapping-declaration-property" revision="2">
<title>property</title>
<para>
<literal>&lt;property&gt;</literal>元素为类定义了一个持久化的,JavaBean风格的属性。
</para>
<programlistingco>
<areaspec>
<area id="property1" coords="2 70"/>
<area id="property2" coords="3 70"/>
<area id="property3" coords="4 70"/>
<areaset id="property4-5" coords="">
<area id="property4" coords='5 70'/>
<area id="property5" coords='6 70'/>
</areaset>
<area id="property6" coords="7 70"/>
<area id="property7" coords="8 70"/>
<area id="property8" coords="9 70"/>
<area id="property9" coords="10 70"/>
<area id="property10" coords="11 70"/>
<area id="property11" coords="12 70"/>
</areaspec>
<programlisting><![CDATA[<property
name="propertyName"
column="column_name"
type="typename"
update="true|false"
insert="true|false"
formula="arbitrary SQL expression"
access="field|property|ClassName"
lazy="true|false"
unique="true|false"
not-null="true|false"
optimistic-lock="true|false"
node="element-name|@attribute-name|element/@attribute|."
/>]]></programlisting>
<calloutlist>
<callout arearefs="property1">
<para>
<literal>name</literal>: 属性的名字,以小写字母开头。
</para>
</callout>
<callout arearefs="property2">
<para>
<literal>column</literal> (可选 - 默认为属性名字): 对应的数据库字段名。
也可以通过嵌套的<literal>&lt;column&gt;</literal>元素指定。
</para>
</callout>
<callout arearefs="property3">
<para>
<literal>type</literal> (可选): 一个Hibernate类型的名字。
</para>
</callout>
<callout arearefs="property4-5">
<para>
<literal>update, insert</literal> (可选 - 默认为 <literal>true</literal>) :
表明用于<literal>UPDATE</literal> 和/或 <literal>INSERT</literal>
的SQL语句中是否包含这个被映射了的字段。这二者如果都设置为<literal>false</literal>
则表明这是一个“外源性derived”的属性它的值来源于映射到同一个或多个
字段的某些其他属性或者通过一个trigger(触发器)或其他程序。
</para>
</callout>
<callout arearefs="property6">
<para>
<literal>formula</literal> (可选): 一个SQL表达式定义了这个<emphasis>计算
computed</emphasis> 属性的值。计算属性没有和它对应的数据库字段。
</para>
</callout>
<callout arearefs="property7">
<para>
<literal>access</literal> (可选 - 默认值为 <literal>property</literal>):
Hibernate用来访问属性值的策略。
</para>
</callout>
<callout arearefs="property8">
<para>
<literal>lazy</literal> (可选 - 默认为 <literal>false</literal>): 指定
指定实例变量第一次被访问时这个属性是否延迟抓取fetched lazily
需要运行时字节码增强)。
</para>
</callout>
<callout arearefs="property9">
<para>
<literal>unique</literal> (可选): 使用DDL为该字段添加唯一的约束。
此外,这也可以用作<literal>property-ref</literal>的目标属性。
</para>
</callout>
<callout arearefs="property10">
<para>
<literal>not-null</literal> (可选): 使用DDL为该字段添加可否为空nullability的约束。
</para>
</callout>
<callout arearefs="property11">
<para>
<literal>optimistic-lock</literal> (可选 - 默认为 <literal>true</literal>):
指定这个属性在做更新时是否需要获得乐观锁定optimistic lock
换句话说它决定这个属性发生脏数据时版本version的值是否增长。
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
<emphasis>typename</emphasis>可以是如下几种:
</para>
<orderedlist spacing="compact">
<listitem>
<para>
Hibernate基础类型之一比如<literal>integer, string, character,date, timestamp,
float, binary, serializable, object, blob</literal>)。
</para>
</listitem>
<listitem>
<para>
一个Java类的名字这个类属于一种默认基础类型
(比如: <literal>int, float,char, java.lang.String, java.util.Date, java.lang.Integer,
java.sql.Clob</literal>)。
</para>
</listitem>
<listitem>
<para>
一个可以序列化的Java类的名字。
</para>
</listitem>
<listitem>
<para>
一个自定义类型的类的名字。(比如: <literal>com.illflow.type.MyCustomType</literal>)。
</para>
</listitem>
</orderedlist>
<para>
如果你没有指定类型Hibernarte会使用反射来得到这个名字的属性以此来猜测正确的Hibernate类型。
Hibernate会按照规则2,3,4的顺序对属性读取器(getter方法的返回类进行解释。然而这还不够。
在某些情况下你仍然需要<literal>type</literal>属性。(比如,为了区别<literal>Hibernate.DATE</literal>
<literal>Hibernate.TIMESTAMP</literal>,或者为了指定一个自定义类型。)
</para>
<para>
<literal>access</literal>属性用来让你控制Hibernate如何在运行时访问属性。在默认情况下
Hibernate会使用属性的get/set方法对pair。如果你指明<literal>access="field"</literal>,
Hibernate会忽略get/set方法对直接使用反射来访问成员变量。你也可以指定你自己的策略
这就需要你自己实现<literal>org.hibernate.property.PropertyAccessor</literal>接口,
再在access中设置你自定义策略类的名字。
</para>
<para>
衍生属性derive propertie是一个特别强大的特征。这些属性应该定义为只读属性值在装载时计算生成。
你用一个SQL表达式生成计算的结果它会在这个实例转载时翻译成一个SQL查询的<literal>SELECT</literal>
子查询语句。
</para>
<programlisting><![CDATA[
<property name="totalPrice"
formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
WHERE li.productId = p.productId
AND li.customerId = customerId
AND li.orderNumber = orderNumber )"/>]]></programlisting>
<para>
注意,你可以使用实体自己的表,而不用为这个特别的列定义别名(
上面例子中的<literal>customerId</literal>)。同时注意,如果你不喜欢使用属性,
你可以使用嵌套的<literal>&lt;formula&gt;</literal>映射元素。
</para>
</sect2>
<sect2 id="mapping-declaration-manytoone" revision="3">
<title>多对一many-to-one</title>
<para>
通过<literal>many-to-one</literal>元素,可以定义一种常见的与另一个持久化类的关联。
这种关系模型是多对一关联(实际上是一个对象引用-译注):这个表的一个外键引用目标表的
主键字段。
</para>
<programlistingco>
<areaspec>
<area id="manytoone1" coords="2 70"/>
<area id="manytoone2" coords="3 70"/>
<area id="manytoone3" coords="4 70"/>
<area id="manytoone4" coords="5 70"/>
<area id="manytoone5" coords="6 70"/>
<areaset id="manytoone6-7" coords="">
<area id="manytoone6" coords='7 70'/>
<area id="manytoone7" coords='8 70'/>
</areaset>
<area id="manytoone8" coords="9 70"/>
<area id="manytoone9" coords="10 70"/>
<area id="manytoone10" coords="11 70"/>
<area id="manytoone11" coords="12 70"/>
<area id="manytoone12" coords="13 70"/>
<area id="manytoone13" coords="14 70"/>
<area id="manytoone14" coords="15 70"/>
<area id="manytoone15" coords="16 70"/>
</areaspec>
<programlisting><![CDATA[<many-to-one
name="propertyName"
column="column_name"
class="ClassName"
cascade="cascade_style"
fetch="join|select"
update="true|false"
insert="true|false"
property-ref="propertyNameFromAssociatedClass"
access="field|property|ClassName"
unique="true|false"
not-null="true|false"
optimistic-lock="true|false"
lazy="true|proxy|false"
not-found="ignore|exception"
entity-name="EntityName"
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
/>]]></programlisting>
<calloutlist>
<callout arearefs="manytoone1">
<para>
<literal>name</literal>: 属性名。
</para>
</callout>
<callout arearefs="manytoone2">
<para>
<literal>column</literal> (可选): 外间字段名。它也可以通过嵌套的
<literal>&lt;column&gt;</literal>元素指定。
</para>
</callout>
<callout arearefs="manytoone3">
<para>
<literal>class</literal> (可选 - 默认是通过反射得到属性类型): 关联的类的名字。
</para>
</callout>
<callout arearefs="manytoone4">
<para>
<literal>cascade级联</literal> (可选): 指明哪些操作会从父对象级联到关联的对象。
</para>
</callout>
<callout arearefs="manytoone5">
<para>
<literal>fetch</literal> (可选 - 默认为 <literal>select</literal>):
在外连接抓取outer-join fetching和序列选择抓取sequential select fetching两者中选择其一。
</para>
</callout>
<callout arearefs="manytoone6-7">
<para>
<literal>update, insert</literal> (可选 - defaults to <literal>true</literal>)
指定对应的字段是否包含在用于<literal>UPDATE</literal> 和/或 <literal>INSERT</literal>
的SQL语句中。如果二者都是<literal>false</literal>,则这是一个纯粹的
“外源性derived”关联它的值是通过映射到同一个或多个字段的某些其他属性得到
或者通过trigger(触发器)、或其他程序。
</para>
</callout>
<callout arearefs="manytoone8">
<para>
<literal>property-ref</literal>: (可选) 指定关联类的一个属性,这个属性将会和本外键相对应。
如果没有指定,会使用对方关联类的主键。
</para>
</callout>
<callout arearefs="manytoone9">
<para>
<literal>access</literal> (可选 - 默认是 <literal>property</literal>):
Hibernate用来访问属性的策略。
</para>
</callout>
<callout arearefs="manytoone10">
<para>
<literal>unique</literal> (可选): 使用DDL为外键字段生成一个唯一约束。此外
这也可以用作<literal>property-ref</literal>的目标属性。这使关联同时具有
一对一的效果。
</para>
</callout>
<callout arearefs="manytoone11">
<para>
<literal>not-null</literal> (可选): 使用DDL为外键字段生成一个非空约束。
</para>
</callout>
<callout arearefs="manytoone12">
<para>
<literal>optimistic-lock</literal> (可选 - 默认为 <literal>true</literal>):
指定这个属性在做更新时是否需要获得乐观锁定optimistic lock
换句话说它决定这个属性发生脏数据时版本version的值是否增长。
</para>
</callout>
<callout arearefs="manytoone13">
<para>
<literal>lazy</literal> (可选 - 默认为 <literal>proxy</literal>):
默认情况下,单点关联是经过代理的。<literal>lazy="true"</literal>指定此属性应该在实例变量第一次被访问时应该延迟抓取fetche lazily需要运行时字节码的增强
<literal>lazy="false"</literal>指定此关联总是被预先抓取。
</para>
</callout>
<callout arearefs="manytoone14">
<para>
<literal>not-found</literal> (可选 - 默认为 <literal>exception</literal>):
指定外键引用的数据不存在时如何处理:
<literal>ignore</literal>会将数据不存在作为关联到一个空对象null处理。
</para>
</callout>
<callout arearefs="manytoone15">
<para>
<literal>entity-name</literal> (optional): 被关联的类的实体名。
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
<literal>cascade</literal>属性设置为除了<literal>none</literal>以外任何有意义的值,
它将把特定的操作传播到关联对象中。这个值就代表着Hibernate基本操作的名称
<literal>persist, merge, delete, save-update, evict, replicate, lock, refresh</literal>
以及特别的值<literal>delete-orphan</literal><literal>all</literal>,并且可以用逗号分隔符
来合并这些操作,例如,<literal>cascade="persist,merge,evict"</literal>
<literal>cascade="all,delete-orphan"</literal>。更全面的解释请参考<xref linkend="objectstate-transitive"/>.
</para>
<para>
一个典型的简单<literal>many-to-one</literal>定义例子:
</para>
<programlisting><![CDATA[<many-to-one name="product" class="Product" column="PRODUCT_ID"/>]]></programlisting>
<para>
<literal>property-ref</literal>属性只应该用来对付老旧的数据库系统,
可能有外键指向对方关联表的是个非主键字段(但是应该是一个惟一关键字)的情况下。
这是一种十分丑陋的关系模型。比如说,假设<literal>Product</literal>类有一个惟一的序列号,
它并不是主键。(<literal>unique</literal>属性控制Hibernate通过SchemaExport工具生成DDL的过程。
</para>
<programlisting><![CDATA[<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>]]></programlisting>
<para>
那么关于<literal>OrderItem</literal> 的映射可能是:
</para>
<programlisting><![CDATA[<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>]]></programlisting>
<para>
当然,我们决不鼓励这种用法。
</para>
<para>
如果被引用的唯一主键由关联实体的多个属性组成,你应该在名称为<literal>&lt;properties&gt;</literal>的元素
里面映射所有关联的属性。
</para>
</sect2>
<sect2 id="mapping-declaration-onetoone" revision="2">
<title>一对一</title>
<para>
持久化对象之间一对一的关联关系是通过<literal>one-to-one</literal>元素定义的。
</para>
<programlistingco>
<areaspec>
<area id="onetoone1" coords="2 70"/>
<area id="onetoone2" coords="3 70"/>
<area id="onetoone3" coords="4 70"/>
<area id="onetoone4" coords="5 70"/>
<area id="onetoone5" coords="6 70"/>
<area id="onetoone6" coords="7 70"/>
<area id="onetoone7" coords="8 70"/>
<area id="onetoone8" coords="9 70"/>
<area id="onetoone9" coords="10 70"/>
<area id="onetoone10" coords="11 70"/>
</areaspec>
<programlisting><![CDATA[<one-to-one
name="propertyName"
class="ClassName"
cascade="cascade_style"
constrained="true|false"
fetch="join|select"
property-ref="propertyNameFromAssociatedClass"
access="field|property|ClassName"
formula="any SQL expression"
lazy="true|proxy|false"
entity-name="EntityName"
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
/>]]></programlisting>
<calloutlist>
<callout arearefs="onetoone1">
<para>
<literal>name</literal>: 属性的名字。
</para>
</callout>
<callout arearefs="onetoone2">
<para>
<literal>class</literal> (可选 - 默认是通过反射得到的属性类型):被关联的类的名字。
</para>
</callout>
<callout arearefs="onetoone3">
<para>
<literal>cascade(级联)</literal> (可选) 表明操作是否从父对象级联到被关联的对象。
</para>
</callout>
<callout arearefs="onetoone4">
<para>
<literal>constrained(约束)</literal> (可选) 表明该类对应的表对应的数据库表,和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。
这个选项影响<literal>save()</literal><literal>delete()</literal>在级联执行时的先后顺序以及
决定该关联能否被委托(也在schema export tool中被使用).
</para>
</callout>
<callout arearefs="onetoone5">
<para>
<literal>fetch</literal> (可选 - 默认设置为<literal>选择</literal>):
在外连接抓取或者序列选择抓取选择其一.
</para>
</callout>
<callout arearefs="onetoone6">
<para>
<literal>property-ref</literal>: (可选) 指定关联类的属性名,这个属性将会和本类的主键相对应。如果没有指定,会使用对方关联类的主键。
</para>
</callout>
<callout arearefs="onetoone7">
<para>
<literal>access</literal> (可选 - 默认是 <literal>property</literal>): Hibernate用来访问属性的策略。
</para>
</callout>
<callout arearefs="onetoone8">
<para>
<literal>formula </literal> (可选):绝大多数一对一的关联都指向其实体的主键。在一些少见的情况中, 你可能会指向其他的一个或多个字段或者是一个表达式这些情况下你可以用一个SQL公式来表示。 可以在org.hibernate.test.onetooneformula找到例子
</para>
</callout>
<callout arearefs="onetoone9">
<para>
<literal>lazy</literal> (可选 - 默认为 <literal>proxy</literal>):
默认情况下,单点关联是经过代理的。<literal>lazy="true"</literal>指定此属性应该在实例变量第一次被访问时应该延迟抓取fetche lazily需要运行时字节码的增强
<literal>lazy="false"</literal>指定此关联总是被预先抓取。<emphasis>注意,如果<literal>constrained="false"</literal>,
不可能使用代理Hibernate会采取预先抓取</emphasis>
</para>
</callout>
<callout arearefs="onetoone10">
<para>
<literal>entity-name</literal> (可选): 被关联的类的实体名。
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
有两种不同的一对一关联:
</para>
<itemizedlist>
<listitem>
<para>
主键关联
</para></listitem>
<listitem>
<para>
惟一外键关联
</para></listitem>
</itemizedlist>
<para>
主键关联不需要额外的表字段;如果两行是通过这种一对一关系相关联的,那么这两行就共享同样的主关键字值。所以如果你希望两个对象通过主键一对一关联,你必须确认它们被赋予同样的标识值!
</para>
<para>
比如说,对下面的<literal>Employee</literal><literal>Person</literal>进行主键一对一关联:
</para>
<programlisting><![CDATA[<one-to-one name="person" class="Person"/>]]></programlisting>
<programlisting><![CDATA[<one-to-one name="employee" class="Employee" constrained="true"/>]]></programlisting>
<para>
现在我们必须确保PERSON和EMPLOYEE中相关的字段是相等的。我们使用一个被成为<literal>foreign</literal>的特殊的hibernate标识符生成策略
</para>
<programlisting><![CDATA[<class name="person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="foreign">
<param name="property">employee</param>
</generator>
</id>
...
<one-to-one name="employee"
class="Employee"
constrained="true"/>
</class>]]></programlisting>
<para>
一个刚刚保存的<literal>Person</literal>实例被赋予和该<literal>Person</literal><literal>employee</literal>属性所指向的<literal>Employee</literal>实例同样的关键字值。
</para>
<para>
另一种方式是一个外键和一个惟一关键字对应,上面的<literal>Employee</literal><literal>Person</literal>的例子,如果使用这种关联方式,可以表达成:
</para>
<programlisting><![CDATA[<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>]]></programlisting>
<para>
如果在<literal>Person</literal>的映射加入下面几句,这种关联就是双向的:
</para>
<programlisting><![CDATA[<one-to-one name"employee" class="Employee" property-ref="person"/>]]></programlisting>
</sect2>
<sect2 id="mapping-declaration-component" revision="2">
<title>组件(component), 动态组件(dynamic-component)</title>
<para>
<literal>&lt;component&gt;</literal>元素把子对象的一些元素与父类对应的表的一些字段映射起来。 然后组件可以定义它们自己的属性、组件或者集合。参见后面的“Components”一章。
</para>
<programlistingco>
<areaspec>
<area id="component1" coords="2 45"/>
<area id="component2" coords="3 45"/>
<area id="component3" coords="4 45"/>
<area id="component4" coords="5 45"/>
<area id="component5" coords="6 45"/>
<area id="component6" coords="7 45"/>
<area id="component7" coords="8 45"/>
<area id="component8" coords="9 45"/>
</areaspec>
<programlisting><![CDATA[<component
name="propertyName"
class="className"
insert="true|false"
update="true|false"
access="field|property|ClassName"
lazy="true|false"
optimistic-lock="true|false"
unique="true|false"
node="element-name|."
>
<property ...../>
<many-to-one .... />
........
</component>]]></programlisting>
<calloutlist>
<callout arearefs="component1">
<para>
<literal>name</literal>: 属性名
</para>
</callout>
<callout arearefs="component2">
<para>
<literal>class</literal> (可选 - 默认为通过反射得到的属性类型):组件(子)类的名字。
</para>
</callout>
<callout arearefs="component3">
<para>
<literal>insert</literal>: 被映射的字段是否出现在SQL的<literal>INSERT</literal>语句中?
</para>
</callout>
<callout arearefs="component4">
<para>
<literal>update</literal>: 被映射的字段是否出现在SQL的<literal>UPDATE</literal>语句中?
</para>
</callout>
<callout arearefs="component5">
<para>
<literal>access</literal> (可选 - 默认是 <literal>property</literal>): Hibernate用来访问属性的策略。
</para>
</callout>
<callout arearefs="component6">
<para>
<literal>lazy</literal> (可选 - 默认是 <literal>false</literal>): 表明此组件应在实例变量第一次被访问的时候延迟加载(需要编译时字节码装置器)
</para>
</callout>
<callout arearefs="component7">
<para>
<literal>optimistic-lock</literal> (可选 - 默认是 <literal>true</literal>):表明更新此组件是否需要获取乐观锁。换句话说,当这个属性变脏时,是否增加版本号(Version)
</para>
</callout>
<callout arearefs="component8">
<para>
<literal>unique</literal> (可选 - 默认是 <literal>false</literal>):表明组件映射的所有字段上都有唯一性约束
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
<literal>&lt;property&gt;</literal>子标签为子类的一些属性与表字段之间建立映射。
</para>
<para>
<literal>&lt;component&gt;</literal>元素允许加入一个<literal>&lt;parent&gt;</literal>子元素,在组件类内部就可以有一个指向其容器的实体的反向引用。
</para>
<para>
<literal>&lt;dynamic-component&gt;</literal>元素允许把一个<literal>Map</literal>映射为组件其属性名对应map的键值。
参见<xref linkend="components-dynamic"/>.
</para>
</sect2>
<sect2 id="mapping-declaration-properties" revision="2">
<title>properties</title>
<para>
<literal>&lt;properties&gt;</literal> 元素允许定义一个命名的逻辑分组(grouping)包含一个类中的多个属性。
这个元素最重要的用处是允许多个属性的组合作为<literal>property-ref</literal>的目标(target)。
这也是定义多字段唯一约束的一种方便途径。
</para>
<programlistingco>
<areaspec>
<area id="properties1" coords="2 45"/>
<area id="properties2" coords="3 45"/>
<area id="properties3" coords="4 45"/>
<area id="properties4" coords="5 45"/>
<area id="properties5" coords="6 45"/>
</areaspec>
<programlisting><![CDATA[<properties
name="logicalName"
insert="true|false"
update="true|false"
optimistic-lock="true|false"
unique="true|false"
>
<property ...../>
<many-to-one .... />
........
</properties>]]></programlisting>
<calloutlist>
<callout arearefs="properties1">
<para>
<literal>name</literal>: 分组的逻辑名称 -
<emphasis>不是</emphasis> 实际属性的名称.
</para>
</callout>
<callout arearefs="properties2">
<para>
<literal>insert</literal>: 被映射的字段是否出现在SQL的
<literal>INSERT</literal>语句中?
</para>
</callout>
<callout arearefs="properties3">
<para>
<literal>update</literal>: 被映射的字段是否出现在SQL的
<literal>UPDATE</literal>语句中?
</para>
</callout>
<callout arearefs="properties4">
<para>
<literal>optimistic-lock</literal> (可选 - 默认是 <literal>true</literal>):表明更新此组件是否需要获取乐观锁。换句话说,当这个属性变脏时,是否增加版本号(Version)
</para>
</callout>
<callout arearefs="properties5">
<para>
<literal>unique</literal> (可选 - 默认是 <literal>false</literal>):表明组件映射的所有字段上都有唯一性约束
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
例如,如果我们有如下的<literal>&lt;properties&gt;</literal>映射:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="personNumber"/>
...
<properties name="name"
unique="true" update="false">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</properties>
</class>]]></programlisting>
<para>
然后,我们可能有一些遗留的数据关联,引用 <literal>Person</literal>表的这个唯一键,而不是主键。
</para>
<programlisting><![CDATA[<many-to-one name="person"
class="Person" property-ref="name">
<column name="firstName"/>
<column name="initial"/>
<column name="lastName"/>
</many-to-one>]]></programlisting>
<para>
我们并不推荐这样使用,除非在映射遗留数据的情况下。
</para>
</sect2>
<sect2 id="mapping-declaration-subclass" revision="3">
<title>子类(subclass)</title>
<para>
最后,多态持久化需要为父类的每个子类都进行定义。对于“每一棵类继承树对应一个表”的策略来说,就需要使用<literal>&lt;subclass&gt;</literal>定义。
</para>
<programlistingco>
<areaspec>
<area id="subclass1" coords="2 55"/>
<area id="subclass2" coords="3 55"/>
<area id="subclass3" coords="4 55"/>
<area id="subclass4" coords="5 55"/>
</areaspec>
<programlisting><![CDATA[<subclass
name="ClassName"
discriminator-value="discriminator_value"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
entity-name="EntityName"
node="element-name">
<property .... />
.....
</subclass>]]></programlisting>
<calloutlist>
<callout arearefs="subclass1">
<para>
<literal>name</literal>: 子类的全限定名。
</para>
</callout>
<callout arearefs="subclass2">
<para>
<literal>discriminator-value(辨别标志)</literal> (可选 - 默认为类名):一个用于区分每个独立的子类的值。
</para>
</callout>
<callout arearefs="subclass3">
<para>
<literal>proxy(代理)</literal> (可选): 指定一个类或者接口,在延迟装载时作为代理使用。
</para>
</callout>
<callout arearefs="subclass4">
<para>
<literal>lazy</literal> (可选, 默认是<literal>true</literal>): 设置为
<literal>lazy="false"</literal> 禁止使用延迟抓取
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
每个子类都应该定义它自己的持久化属性和子类。
<literal>&lt;version&gt;</literal><literal>&lt;id&gt;</literal> 属性可以从根父类继承下来。在一棵继承树上的每个子类都必须定义一个唯一的<literal>discriminator-value</literal>。如果没有指定就会使用Java类的全限定名。
</para>
<para>
可以在单独的映射文件中,直接在<literal>hibernate-mapping</literal>下定义<literal>subclass</literal><literal>union-subclass</literal><literal>joined-subclass</literal>映射。这样你只要增加一个新的映射文件就可以继承一棵类继承树。你必须在子类的映射中指定<literal>extends</literal> 属性来指定已映射的超类。注意以前这个特性使得映射文件的顺序变得很重要。从Hibernate3开始当使用extends关键字的时候映射文件的次序便不重要了。而在单一映射文件中依旧需要保持将超类定义在子类之前这样的次序。
</para>
<programlisting><![CDATA[
<hibernate-mapping>
<subclass name="DomesticCat" extends="Cat" discriminator-value="D">
<property name="name" type="string"/>
</subclass>
</hibernate-mapping>]]></programlisting>
<para>
更多关于继承映射的信息, 参考 <xref linkend="inheritance"/>章节.
</para>
</sect2>
<sect2 id="mapping-declaration-joinedsubclass" revision="3">
<title>连接的子类(joined-subclass)</title>
<para>
此外,每个子类可能被映射到他自己的表中(每个子类一个表的策略)。被继承的状态通过和超类的表关联得到。我们使用<literal>&lt;joined-subclass&gt;</literal>元素。
</para>
<programlistingco>
<areaspec>
<area id="joinedsubclass1" coords="2 45"/>
<area id="joinedsubclass2" coords="3 45"/>
<area id="joinedsubclass3" coords="4 45"/>
<area id="joinedsubclass4" coords="5 45"/>
</areaspec>
<programlisting><![CDATA[<joined-subclass
name="ClassName"
table="tablename"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
schema="schema"
catalog="catalog"
extends="SuperclassName"
persister="ClassName"
subselect="SQL expression"
entity-name="EntityName"
node="element-name">
<key .... >
<property .... />
.....
</joined-subclass>]]></programlisting>
<calloutlist>
<callout arearefs="joinedsubclass1">
<para>
<literal>name</literal>: 子类的全限定名。
</para>
</callout>
<callout arearefs="joinedsubclass2">
<para>
<literal>table</literal>: 子类的表名.
</para>
</callout>
<callout arearefs="joinedsubclass3">
<para>
<literal>proxy</literal> (可选): 指定一个类或者接口,在延迟装载时作为代理使用。
</para>
</callout>
<callout arearefs="joinedsubclass4">
<para>
<literal>lazy</literal> (可选, 默认是 <literal>true</literal>): 设置为
<literal>lazy="false"</literal> 禁止使用延迟装载。
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
这种映射策略不需要指定辨别标志(discriminator)字段。但是,每一个子类都必须使用<literal>&lt;key&gt;</literal>元素指定一个表字段来持有对象的标识符。本章开始的映射可以被用如下方式重写:
</para>
<programlisting><![CDATA[<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat" table="CATS">
<id name="id" column="uid" type="long">
<generator class="hilo"/>
</id>
<property name="birthdate" type="date"/>
<property name="color" not-null="true"/>
<property name="sex" not-null="true"/>
<property name="weight"/>
<many-to-one name="mate"/>
<set name="kittens">
<key column="MOTHER"/>
<one-to-many class="Cat"/>
</set>
<joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
<key column="CAT"/>
<property name="name" type="string"/>
</joined-subclass>
</class>
<class name="eg.Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping>]]></programlisting>
<para>
更多关于继承映射的信息,参考<xref linkend="inheritance"/>
</para>
</sect2>
<sect2 id="mapping-declaration-unionsubclass" revision="2">
<title>联合子类(union-subclass)</title>
<para>
第三种选择是仅仅映射类继承树中具体类部分到表中(每个具体类一张表的策略)。其中,每张表定义了类的所有持久化状态,包括继承的状态。在 Hibernate 中,并不需要完全显式地映射这样的继承树。你可以简单地使用单独的<literal>&lt;class&gt;</literal>定义映射每个类。然而,如果你想使用多态关联(例如,一个对类继承树中超类的关联),你需要使用<literal>&lt;union-subclass&gt;</literal>映射。
</para>
<programlistingco>
<areaspec>
<area id="unionsubclass1" coords="2 45"/>
<area id="unionsubclass2" coords="3 45"/>
<area id="unionsubclass3" coords="4 45"/>
<area id="unionsubclass4" coords="5 45"/>
</areaspec>
<programlisting><![CDATA[<union-subclass
name="ClassName"
table="tablename"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
schema="schema"
catalog="catalog"
extends="SuperclassName"
abstract="true|false"
persister="ClassName"
subselect="SQL expression"
entity-name="EntityName"
node="element-name">
<property .... />
.....
</union-subclass>]]></programlisting>
<calloutlist>
<callout arearefs="unionsubclass1">
<para>
<literal>name</literal>: 子类的全限定名。
</para>
</callout>
<callout arearefs="unionsubclass2">
<para>
<literal>table</literal>: 子类的表名
</para>
</callout>
<callout arearefs="unionsubclass3">
<para>
<literal>proxy</literal> (可选): 指定一个类或者接口,在延迟装载时作为代理使用。
</para>
</callout>
<callout arearefs="unionsubclass4">
<para>
<literal>lazy</literal> (可选, 默认是 <literal>true</literal>): 设置为
<literal>lazy="false"</literal> 禁止使用延迟装载。
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
这种映射策略不需要指定辨别标志(discriminator)字段。
</para>
<para>
更多关于继承映射的信息,参考<xref linkend="inheritance"/>
</para>
</sect2>
<sect2 id="mapping-declaration-join" revision="3">
<title>连接(join)</title>
<para>
使用 <literal>&lt;join&gt;</literal> 元素,可以将一个类的属性映射到多张表中。
</para>
<programlistingco>
<areaspec>
<area id="join1" coords="2 50"/>
<area id="join2" coords="3 50"/>
<area id="join3" coords="4 50"/>
<area id="join4" coords="5 50"/>
<area id="join5" coords="6 50"/>
<area id="join6" coords="7 50"/>
</areaspec>
<programlisting><![CDATA[<join
table="tablename"
schema="owner"
catalog="catalog"
fetch="join|select"
inverse="true|false"
optional="true|false">
<key ... />
<property ... />
...
</join>]]></programlisting>
<calloutlist>
<callout arearefs="join1">
<para>
<literal>table</literal>: 被连接表的名称。
</para>
</callout>
<callout arearefs="join2">
<para>
<literal>schema</literal> (可选):覆盖由根<literal>&lt;hibernate-mapping&gt;</literal>元素指定的模式名称。
</para>
</callout>
<callout arearefs="join3">
<para>
<literal>catalog</literal> (可选): 覆盖由根 <literal>&lt;hibernate-mapping&gt;</literal>元素指定的目录名称。
</para>
</callout>
<callout arearefs="join4">
<para>
<literal>fetch</literal> (可选 - 默认是 <literal>join</literal>):
如果设置为默认值<literal>join</literal> Hibernate 将使用一个内连接来得到这个类或其超类定义的<literal>&lt;join&gt;</literal>,而使用一个外连接来得到其子类定义的<literal>&lt;join&gt;</literal>。如果设置为<literal>select</literal>,则 Hibernate 将为子类定义的 <literal>&lt;join&gt;</literal>使用顺序选择。这仅在一行数据表示一个子类的对象的时候才会发生。对这个类和其超类定义的<literal>&lt;join&gt;</literal>,依然会使用内连接得到。
</para>
</callout>
<callout arearefs="join5">
<para>
<literal>inverse</literal> (可选 - 默认是 <literal>false</literal>):
如果打开Hibernate 不会插入或者更新此连接定义的属性。
</para>
</callout>
<callout arearefs="join6">
<para>
<literal>optional</literal> (可选 - 默认是 <literal>false</literal>):
如果打开Hibernate 只会在此连接定义的属性非空时插入一行数据,并且总是使用一个外连接来得到这些属性。
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
例如,一个人(person)的地址(address)信息可以被映射到单独的表中(并保留所有属性的值类型语义)
</para>
<programlisting><![CDATA[<class name="Person"
table="PERSON">
<id name="id" column="PERSON_ID">...</id>
<join table="ADDRESS">
<key column="ADDRESS_ID"/>
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</join>
...]]></programlisting>
<para>
此特性常常对遗留数据模型有用,我们推荐表个数比类个数少,以及细粒度的领域模型。然而,在单独的继承树上切换继承映射策略是有用的,后面会解释这点。
</para>
</sect2>
<sect2 id="mapping-declaration-key">
<title>键(key)</title>
<para>
我们目前已经见到过<literal>&lt;key&gt;</literal>元素多次了。 这个元素在父映射元素定义了对新表的连接,并且在被连接表中定义了一个外键引用原表的主键的情况下经常使用。
</para>
<programlistingco>
<areaspec>
<area id="key1" coords="2 50"/>
<area id="key2" coords="3 50"/>
<area id="key3" coords="4 50"/>
<area id="key4" coords="5 50"/>
<area id="key5" coords="6 50"/>
<area id="key6" coords="7 50"/>
</areaspec>
<programlisting><![CDATA[<key
column="columnname"
on-delete="noaction|cascade"
property-ref="propertyName"
not-null="true|false"
update="true|false"
unique="true|false"
/>]]></programlisting>
<calloutlist>
<callout arearefs="key1">
<para>
<literal>column</literal> (可选): 外键字段的名称。也可以通过嵌套的 <literal>&lt;column&gt;</literal>指定。
</para>
</callout>
<callout arearefs="key2">
<para>
<literal>on-delete</literal> (可选, 默认是 <literal>noaction</literal>):
表明外键关联是否打开数据库级别的级联删除。
</para>
</callout>
<callout arearefs="key3">
<para>
<literal>property-ref</literal> (可选): 表明外键引用的字段不是原表的主键(提供给遗留数据)。
</para>
</callout>
<callout arearefs="key4">
<para>
<literal>not-null</literal> (可选): 表明外键的字段不可为空(这意味着无论何时外键都是主键的一部分)。
</para>
</callout>
<callout arearefs="key5">
<para>
<literal>update</literal> (可选): 表明外键决不应该被更新(这意味着无论何时外键都是主键的一部分)。
</para>
</callout>
<callout arearefs="key6">
<para>
<literal>unique</literal> (可选): 表明外键应有唯一性约束 (这意味着无论何时外键都是主键的一部分)。
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
对那些看重删除性能的系统,我们推荐所有的键都应该定义为<literal>on-delete="cascade"</literal>,这样 Hibernate 将使用数据库级的<literal>ON CASCADE DELETE</literal>约束,而不是多个<literal>DELETE</literal>语句。 注意,这个特性会绕过 Hibernate 通常对版本数据(versioned data)采用的乐观锁策略。
</para>
<para>
<literal>not-null</literal><literal>update</literal> 属性在映射单向一对多关联的时候有用。如果你映射一个单向一对多关联到非空的(non-nullable)外键,你<emphasis>必须</emphasis><literal>&lt;key not-null="true"&gt;</literal>定义此键字段。
</para>
</sect2>
<sect2 id="mapping-column" revision="3">
<title>字段和规则元素column and formula elements</title>
<para>
任何接受<literal>column</literal>属性的映射元素都可以选择接受<literal>&lt;column&gt;</literal> 子元素。同样的,<literal>formula</literal>也可以替换<literal>&lt;formula&gt;</literal>属性。
</para>
<programlisting><![CDATA[<column
name="column_name"
length="N"
precision="N"
scale="N"
not-null="true|false"
unique="true|false"
unique-key="multicolumn_unique_key_name"
index="index_name"
sql-type="sql_type_name"
check="SQL expression"/>]]></programlisting>
<programlisting><![CDATA[<formula>SQL expression</formula>]]></programlisting>
<para>
<literal>column</literal><literal>formula</literal> 属性甚至可以在同一个属性或关联映射中被合并来表达,例如,一些奇异的连接条件。
</para>
<programlisting><![CDATA[<many-to-one name="homeAddress" class="Address"
insert="false" update="false">
<column name="person_id" not-null="true" length="10"/>
<formula>'MAILING'</formula>
</many-to-one>]]></programlisting>
</sect2>
<sect2 id="mapping-declaration-import">
<title>引用(import)</title>
<para>
假设你的应用程序有两个同样名字的持久化类但是你不想在Hibernate查询中使用他们的全限定名。除了依赖<literal>auto-import="true"</literal>以外类也可以被显式地“import(引用)”。你甚至可以引用没有明确被映射的类和接口。
</para>
<programlisting><![CDATA[<import class="java.lang.Object" rename="Universe"/>]]></programlisting>
<programlistingco>
<areaspec>
<area id="import1" coords="2 40"/>
<area id="import2" coords="3 40"/>
</areaspec>
<programlisting><![CDATA[<import
class="ClassName"
rename="ShortName"
/>]]></programlisting>
<calloutlist>
<callout arearefs="import1">
<para>
<literal>class</literal>: 任何Java类的全限定名。
</para>
</callout>
<callout arearefs="import2">
<para>
<literal>rename</literal> (可选 - 默认为类的全限定名):
在查询语句中可以使用的名字。
</para>
</callout>
</calloutlist>
</programlistingco>
</sect2>
<sect2 id="mapping-types-anymapping" revision="2">
<title>any</title>
<para>
这是属性映射的又一种类型。<literal>&lt;any&gt;</literal> 映射元素定义了一种从多个表到类的多态关联。这种类型的映射常常需要多于一个字段。第一个字段持有被关联实体的类型,其他的字段持有标识符。对这种类型的关联来说,不可能指定一个外键约束,所以这当然不是映射(多态)关联的通常的方式。你只应该在非常特殊的情况下使用它(比如审计log用户会话数据等等)。
</para>
<para>
<literal>meta-type</literal> 属性使得应用程序能指定一个将数据库字段的值映射到持久化类的自定义类型。这个持久化类包含有用<literal>id-type</literal>指定的标识符属性。
你必须指定从meta-type的值到类名的映射。
</para>
<programlisting><![CDATA[<any name="being" id-type="long" meta-type="string">
<meta-value value="TBL_ANIMAL" class="Animal"/>
<meta-value value="TBL_HUMAN" class="Human"/>
<meta-value value="TBL_ALIEN" class="Alien"/>
<column name="table_name"/>
<column name="id"/>
</any>]]></programlisting>
<programlistingco>
<areaspec>
<area id="any1" coords="2 50"/>
<area id="any2" coords="3 50"/>
<area id="any3" coords="4 50"/>
<area id="any4" coords="5 50"/>
<area id="any5" coords="6 50"/>
<area id="any6" coords="7 50"/>
</areaspec>
<programlisting><![CDATA[<any
name="propertyName"
id-type="idtypename"
meta-type="metatypename"
cascade="cascade_style"
access="field|property|ClassName"
optimistic-lock="true|false"
>
<meta-value ... />
<meta-value ... />
.....
<column .... />
<column .... />
.....
</any>]]></programlisting>
<calloutlist>
<callout arearefs="any1">
<para>
<literal>name</literal>: 属性名
</para>
</callout>
<callout arearefs="any2">
<para>
<literal>id-type</literal>: 标识符类型
</para>
</callout>
<callout arearefs="any3">
<para>
<literal>meta-type</literal> (可选 -默认是 <literal>string</literal>):
允许辨别标志(discriminator)映射的任何类型
</para>
</callout>
<callout arearefs="any4">
<para>
<literal>cascade</literal> (可选 -默认是<literal>none</literal>):
级联的类型
</para>
</callout>
<callout arearefs="any5">
<para>
<literal>access</literal> (可选 -默认是 <literal>property</literal>): Hibernate 用来访问属性值的策略。
</para>
</callout>
<callout arearefs="any6">
<para>
<literal>optimistic-lock</literal> (可选 -默认是 <literal>true</literal>): 表明更新此组件是否需要获取乐观锁。换句话说,当这个属性变脏时,是否增加版本号(Version)
</para>
</callout>
</calloutlist>
</programlistingco>
</sect2>
</sect1>
<sect1 id="mapping-types">
<title>Hibernate 的类型</title>
<sect2 id="mapping-types-entitiesvalues" revision="1">
<title>实体(Entities)和值(values)</title>
<para>
为了理解很多与持久化服务相关的Java语言级对象的行为我们需要把它们分为两类
</para>
<para>
<emphasis>实体entity</emphasis> 独立于任何持有实体引用的对象。与通常的Java模型相比不再被引用的对象会被当作垃圾收集掉。实体必须被显式的保存和删除(除非保存和删除是从父实体向子实体引发的<emphasis>级联</emphasis>)。这和ODMG模型中关于对象通过可触及保持持久性有一些不同——比较起来更加接近应用程序对象通常在一个大系统中的使用方法。实体支持循环引用和交叉引用它们也可以加上版本信息。
</para>
<para>
一个实体的持久状态包含指向其他实体和<emphasis></emphasis>类型实例的引用。值可以是原始类型,集合(不是集合中的对象),组件或者特定的不可变对象。与实体不同,值(特别是集合和组件)是通过可触及性来进行持久化和删除的。因为值对象(和原始类型数据)是随着包含他们的实体而被持久化和删除的,他们不能被独立的加上版本信息。值没有独立的标识,所以他们不能被两个实体或者集合共享。
</para>
<para>
直到现在,我们都一直使用术语“持久类”(persistent class)来代表实体。我们仍然会这么做。 然而严格说来,不是所有的用户自定义的,带有持久化状态的类都是实体。<emphasis>组件</emphasis>就是用户自定义类,却是值语义的。<literal>java.lang.String</literal>类型的java属性也是值语义的。给了这个定义以后我们可以说所有JDK提供的类型(类)都是值类型的语义,而用于自定义类型可能被映射为实体类型或值类型语义。采用哪种类型的语义取决于开发人员。在领域模型中,寻找实体类的一个好线索是共享引用指向这个类的单一实例,而组合或聚合通常被转化为值类型。
</para>
<para>
我们会在本文档中重复碰到这两个概念。
</para>
<para>
挑战在于将java类型系统(和开发者定义的实体和值类型)映射到 SQL/数据库类型系统。Hibernate提供了连接两个系统之间的桥梁对于实体类型我们使用<literal>&lt;class&gt;</literal>, <literal>&lt;subclass&gt;</literal> 等等。对于值类型,我们使用 <literal>&lt;property&gt;</literal>, <literal>&lt;component&gt;</literal> 及其他,通常跟随着<literal>type</literal>属性。这个属性的值是Hibernate 的<emphasis>映射类型</emphasis>的名字。Hibernate提供了许多现成的映射(标准的JDK值类型)。你也可以编写自己的映射类型并实现自定义的变换策略,随后我们会看到这点。
</para>
<para>
所有的Hibernate内建类型除了collections以外都支持空(null)语义。
</para>
</sect2>
<sect2 id="mapping-types-basictypes" revision="2">
<title>基本值类型</title>
<para>
The built-in <emphasis>basic mapping types</emphasis> may be roughly categorized into
内建的 <emphasis>基本映射类型</emphasis>可以大致分为
<variablelist>
<varlistentry>
<term><literal>integer, long, short, float, double, character, byte,
boolean, yes_no, true_false</literal></term>
<listitem>
<para>
这些类型都对应Java的原始类型或者其封装类来符合(特定厂商的)SQL 字段类型。<literal>boolean, yes_no</literal><literal>true_false</literal>都是Java 中<literal>boolean</literal> 或者<literal>java.lang.Boolean</literal>的另外说法。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>string</literal></term>
<listitem>
<para>
<literal>java.lang.String</literal>
<literal>VARCHAR</literal> (或者 Oracle的 <literal>VARCHAR2</literal>)的映射。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>date, time, timestamp</literal></term>
<listitem>
<para>
<literal>java.util.Date</literal>和其子类到SQL类型<literal>DATE</literal>, <literal>TIME</literal><literal>TIMESTAMP</literal> (或等价类型)的映射。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>calendar, calendar_date</literal></term>
<listitem>
<para>
<literal>java.util.Calendar</literal> 到SQL 类型<literal>TIMESTAMP</literal><literal>DATE</literal>(或等价类型)的映射。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>big_decimal, big_integer</literal></term>
<listitem>
<para>
<literal>java.math.BigDecimal</literal><literal>java.math.BigInteger</literal><literal>NUMERIC</literal> (或者 Oracle 的<literal>NUMBER</literal>类型)的映射。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>locale, timezone, currency</literal></term>
<listitem>
<para>
<literal>java.util.Locale</literal>, <literal>java.util.TimeZone</literal><literal>java.util.Currency</literal><literal>VARCHAR</literal> (或者 Oracle 的<literal>VARCHAR2</literal>类型)的映射.
<literal>Locale</literal><literal>Currency</literal> 的实例被映射为它们的ISO代码。<literal>TimeZone</literal>的实例被影射为它的<literal>ID</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>class</literal></term>
<listitem>
<para>
<literal>java.lang.Class</literal>
<literal>VARCHAR</literal> (或者 Oracle 的<literal>VARCHAR2</literal>类型)的映射。<literal>Class</literal>被映射为它的全限定名。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>binary</literal></term>
<listitem>
<para>
把字节数组(byte arrays)映射为对应的 SQL二进制类型。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>text</literal></term>
<listitem>
<para>
把长Java字符串映射为SQL的<literal>CLOB</literal>或者<literal>TEXT</literal>类型。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>serializable</literal></term>
<listitem>
<para>
把可序列化的Java类型映射到对应的SQL二进制类型。你也可以为一个并非默认为基本类型的可序列化Java类或者接口指定Hibernate类型<literal>serializable</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>clob, blob</literal></term>
<listitem>
<para>
JDBC 类 <literal>java.sql.Clob</literal><literal>java.sql.Blob</literal>的映射。某些程序可能不适合使用这个类型因为blob和clob对象可能在一个事务之外是无法重用的。(而且, 驱动程序对这种类型的支持充满着补丁和前后矛盾。)
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
实体及其集合的唯一标识可以是除了<literal>binary</literal><literal>blob</literal><literal>clob</literal>之外的任何基础类型。(联合标识也是允许的,后面会说到。)
</para>
<para>
<literal>org.hibernate.Hibernate</literal>中,定义了基础类型对应的<literal>Type</literal>常量。比如,<literal>Hibernate.STRING</literal>代表<literal>string</literal> 类型。
</para>
</sect2>
<sect2 id="mapping-types-custom" revision="2">
<title>自定义值类型</title>
<para>
开发者创建属于他们自己的值类型也是很容易的。比如说,你可能希望持久化<literal>java.lang.BigInteger</literal>类型的属性,持久化成为<literal>VARCHAR</literal>字段。Hibernate没有内置这样一种类型。自定义类型能够映射一个属性(或集合元素)到不止一个数据库表字段。比如说你可能有这样的Java属性<literal>getName()</literal>/<literal>setName()</literal>,这是<literal>java.lang.String</literal>类型的,对应的持久化到三个字段:<literal>FIRST_NAME</literal>, <literal>INITIAL</literal>, <literal>SURNAME</literal>
</para>
<para>
要实现一个自定义类型,可以实现<literal>org.hibernate.UserType</literal><literal>org.hibernate.CompositeUserType</literal>中的任一个并且使用类型的Java全限定类名来定义属性。请查看<literal>org.hibernate.test.DoubleStringType</literal>这个例子,看看它是怎么做的。
</para>
<programlisting><![CDATA[<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
<column name="first_string"/>
<column name="second_string"/>
</property>]]></programlisting>
<para>
注意使用<literal>&lt;column&gt;</literal>标签来把一个属性映射到多个字段的做法。
</para>
<para>
<literal>CompositeUserType</literal>, <literal>EnhancedUserType</literal>,
<literal>UserCollectionType</literal>, 和 <literal>UserVersionType</literal> 接口为更特殊的使用方式提供支持。
</para>
<para>
你甚至可以在一个映射文件中提供参数给一个<literal>UserType</literal>。 为了这样做,你的<literal>UserType</literal>必须实现<literal>org.hibernate.usertype.ParameterizedType</literal>接口。为了给自定义类型提供参数,你可以在映射文件中使用<literal>&lt;type&gt;</literal>元素。
</para>
<programlisting><![CDATA[<property name="priority">
<type name="com.mycompany.usertypes.DefaultValueIntegerType">
<param name="default">0</param>
</type>
</property>]]></programlisting>
<para>
现在,<literal>UserType</literal> 可以从传入的<literal>Properties</literal>对象中得到<literal>default</literal> 参数的值。
</para>
<para>
如果你非常频繁地使用某一<literal>UserType</literal>,可以为他定义一个简称。这可以通过使用 <literal>&lt;typedef&gt;</literal>元素来实现。Typedefs为一自定义类型赋予一个名称并且如果此类型是参数化的还可以包含一系列默认的参数值。
</para>
<programlisting><![CDATA[<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
<param name="default">0</param>
</typedef>]]></programlisting>
<programlisting><![CDATA[<property name="priority" type="default_zero"/>]]></programlisting>
<para>
也可以根据具体案例通过属性映射中的类型参数覆盖在typedef中提供的参数。
</para>
<para>
尽管 Hibernate 内建的丰富的类型和对组件的支持意味着你可能很少 <emphasis>需要</emphasis>使用自定义类型。不过,为那些在你的应用中经常出现的(非实体)类使用自定义类型也是一个好方法。例如,一个<literal>MonetaryAmount</literal>类使用<literal>CompositeUserType</literal>来映射是不错的选择,虽然他可以很容易地被映射成组件。这样做的动机之一是抽象。使用自定义类型,以后假若你改变表示金额的方法时,它可以保证映射文件不需要修改。
</para>
</sect2>
</sect1>
<sect1 id="mapping-quotedidentifiers">
<title>SQL中引号包围的标识符</title>
<para>
你可通过在映射文档中使用反向引号(`)把表名或者字段名包围起来以强制Hibernate在生成的SQL中把标识符用引号包围起来。Hibernate会使用相应的SQL<literal>Dialect</literal>(方言)来使用正确的引号风格(通常是双引号但是在SQL Server中是括号MySQL中是反向引号)。
</para>
<programlisting><![CDATA[<class name="LineItem" table="`Line Item`">
<id name="id" column="`Item Id`"/><generator class="assigned"/></id>
<property name="itemNumber" column="`Item #`"/>
...
</class>]]></programlisting>
</sect1>
<sect1 id="mapping-alternatives">
<title>其他元数据(Metadata)</title>
<para>
XML 并不适用于所有人, 因此有其他定义Hibernate O/R 映射元数据(metadata)的方法。
</para>
<sect2 id="mapping-xdoclet">
<title>使用 XDoclet 标记</title>
<para>
很多Hibernate使用者更喜欢使用XDoclet<literal>@hibernate.tags</literal>将映射信息直接嵌入到源代码中。我们不会在本文档中涉及这个方法因为严格说来这属于XDoclet的一部分。然而我们包含了如下使用XDoclet映射的<literal>Cat</literal>类的例子。
</para>
<programlisting><![CDATA[package eg;
import java.util.Set;
import java.util.Date;
/**
* @hibernate.class
* table="CATS"
*/
public class Cat {
private Long id; // identifier
private Date birthdate;
private Cat mother;
private Set kittens
private Color color;
private char sex;
private float weight;
/*
* @hibernate.id
* generator-class="native"
* column="CAT_ID"
*/
public Long getId() {
return id;
}
private void setId(Long id) {
this.id=id;
}
/**
* @hibernate.many-to-one
* column="PARENT_ID"
*/
public Cat getMother() {
return mother;
}
void setMother(Cat mother) {
this.mother = mother;
}
/**
* @hibernate.property
* column="BIRTH_DATE"
*/
public Date getBirthdate() {
return birthdate;
}
void setBirthdate(Date date) {
birthdate = date;
}
/**
* @hibernate.property
* column="WEIGHT"
*/
public float getWeight() {
return weight;
}
void setWeight(float weight) {
this.weight = weight;
}
/**
* @hibernate.property
* column="COLOR"
* not-null="true"
*/
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
/**
* @hibernate.set
* inverse="true"
* order-by="BIRTH_DATE"
* @hibernate.collection-key
* column="PARENT_ID"
* @hibernate.collection-one-to-many
*/
public Set getKittens() {
return kittens;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kittens.add(kitten);
}
/**
* @hibernate.property
* column="SEX"
* not-null="true"
* update="false"
*/
public char getSex() {
return sex;
}
void setSex(char sex) {
this.sex=sex;
}
}]]></programlisting>
<para>
参考Hibernate网站更多的Xdoclet和Hibernate的例子
</para>
</sect2>
<sect2 id="mapping-annotations">
<title>使用 JDK 5.0 的注解(Annotation)</title>
<para>
JDK 5.0 在语言级别引入了 XDoclet 风格的标注并且是类型安全的在编译期进行检查。这一机制比XDoclet的注解更为强大有更好的工具和IDE支持。例如 IntelliJ IDEA支持JDK 5.0注解的自动完成和语法高亮 。EJB规范的新修订版(JSR-220)使用 JDK 5.0的注解作为entity beans的主要元数据(metadata)机制。Hibernate 3 实现了JSR-220 (the persistence API)的<literal>EntityManager</literal>,支持通过<emphasis>Hibernate Annotations</emphasis>包定义映射元数据。这个包作为单独的部分下载支持EJB3 (JSR-220)和Hibernate3的元数据。
</para>
<para>
这是一个被注解为EJB entity bean 的POJO类的例子
</para>
<programlisting><![CDATA[@Entity(access = AccessType.FIELD)
public class Customer implements Serializable {
@Id;
Long id;
String firstName;
String lastName;
Date birthday;
@Transient
Integer age;
@Dependent
private Address homeAddress;
@OneToMany(cascade=CascadeType.ALL,
targetEntity="Order")
@JoinColumn(name="CUSTOMER_ID")
Set orders;
// Getter/setter and business methods
}]]></programlisting>
<para>
注意:对 JDK 5.0 注解 (和 JSR-220)支持的工作仍然在进行中,并未完成。
</para>
</sect2>
</sect1>
</chapter>