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

344 lines
14 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="components">
<title>组件Component映射</title>
<para>
<emphasis>Component</emphasis>这个概念在Hibernate中几处不同的地方为了不同的目的被重复使用.
</para>
<sect1 id="components-dependentobjects">
<title>依赖对象Dependent objects</title>
<para>
Component是一个被包含的对象,它作为值类型被持久化而非一个被引用的实体。“component(组件)”这一术语指的是面向对象的合成概念(而并不是系统构架层次上的组件的概念)举个例子, 你可以对人Person)如以下这样来建模:
</para>
<programlisting><![CDATA[public class Person {
private java.util.Date birthday;
private Name name;
private String key;
public String getKey() {
return key;
}
private void setKey(String key) {
this.key=key;
}
public java.util.Date getBirthday() {
return birthday;
}
public void setBirthday(java.util.Date birthday) {
this.birthday = birthday;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
......
......
}]]></programlisting>
<programlisting><![CDATA[public class Name {
char initial;
String first;
String last;
public String getFirst() {
return first;
}
void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
void setLast(String last) {
this.last = last;
}
public char getInitial() {
return initial;
}
void setInitial(char initial) {
this.initial = initial;
}
}]]></programlisting>
<para>
现在,<literal>姓名(Name)</literal>是作为<literal>人(Person)</literal>的一个组成部分。需要注意的是:需要对<literal>姓名</literal>
的持久化属性定义getter和setter方法,但是不需要实现任何的接口或申明标识符字段。
</para>
<para>
以下是这个例子的Hibernate映射文件:
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid.hex"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name"> <!-- class attribute optional -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>]]></programlisting>
<para>
人员(Person)表中将包括<literal>pid</literal>,
<literal>birthday</literal>,
<literal>initial</literal>,
<literal>first</literal>
<literal>last</literal>等字段。
</para>
<para>
就像所有的值类型一样, Component不支持共享引用。
换句话说两个人可能重名但是两个person对象应该包含两个独立的name对象只不过是具有“同样”的值。
Component的值为空从语义学上来讲是<emphasis>专有的(ad hoc)</emphasis>。 每当
重新加载一个包含组件的对象,如果component的所有字段为空那么将Hibernate将假定整个component为
空。对于绝大多数目的,这样假定是没有问题的。
</para>
<para>
Component的属性可以是Hibernate类型包括Collections, many-to-one 关联, 以及其它Component
等等。嵌套Component不应该作为特殊的应用被考虑(Nested components should not be considered
an exotic usage)。 Hibernate趋向于支持设计细致(fine-grained)的对象模型。
</para>
<para>
<literal>&lt;component&gt;</literal> 元素还允许有 <literal>&lt;parent&gt;</literal>子元素 用来表明component类中的一个属性返回包含它的实体的引用。
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid.hex"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name" unique="true">>
<parent name="namedPerson"/> <!-- reference back to the Person -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>]]></programlisting>
</sect1>
<sect1 id="components-incollections" revision="1">
<title>在集合中出现的依赖对象</title>
<para>
Hibernate支持component的集合(例如: 一个元素是“姓名”这种类型的数组)。 你可以使用<literal>&lt;composite-element&gt;</literal>标签替代<literal>&lt;element&gt;</literal>标签来定义你的component集合。
</para>
<programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
<key column="id"/>
<composite-element class="eg.Name"> <!-- class attribute required -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</composite-element>
</set>]]></programlisting>
<para>
注意,如果你决定定义一个元素是联合元素的<literal>Set</literal>,正确地实现<literal>equals()</literal><literal>hashCode()</literal>是非常重要的。
</para>
<para>
组合元素可以包含component但是不能包含集合。如果你的组合元素自身包含component, 必须使用<literal>&lt;nested-composite-element&gt;</literal>标签。这是一个相当特殊的案例 - 组合元素的集合自身可以包含component。 这个时候你就应该考虑一下使用one-to-many关联是否会更恰当。 尝试对这个组合元素重新建模为一个实体但是需要注意的是虽然Java模型和重新建模前 是一样的,关系模型和持久性语义上仍然存在轻微的区别。
</para>
<para>
请注意如果你使用<literal>&lt;set&gt;</literal>标签,一个组合元素的映射不支持可能为空的属性. 当删除对象时, Hibernate必须使用每一个字段的来确定一条记录(在组合元素表中,没有单个的关键字段), 如果有为null的字段这样做就不可能了。你必须作出一个选择要么在组合元素中使用不能为空的属性 要么选择使用<literal>&lt;list&gt;</literal>, <literal>&lt;map&gt;</literal>,<literal>&lt;bag&gt;</literal> 或者 <literal>&lt;idbag&gt;</literal>而不是 <literal>&lt;set&gt;</literal>
</para>
<para>
组合元素有个特别的案例,是组合元素可以包含一个<literal>&lt;many-to-one&gt;</literal> 元素。类似这样的映射允许你映射一个many-to-mang关联表作为组合元素额外的字段。(A mapping like this allows you to map extra columns of a many-to-many association table to the composite element class.) 接下来的的例子是从<literal>Order</literal><literal>Item</literal>的一个多对多的关联关系,而 <literal>purchaseDate</literal>, <literal>price</literal><literal>quantity</literal><literal>Item</literal>的关联属性。
</para>
<programlisting><![CDATA[<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.Purchase">
<property name="purchaseDate"/>
<property name="price"/>
<property name="quantity"/>
<many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
</composite-element>
</set>
</class>]]></programlisting>
<para>
当然在另一方面无法存在指向purchase的关联因此不能实现双向关联查询。记住组建是值类型并且不允许共享关联。单个<literal>Purchase</literal> 可以放在包含<literal>Order</literal>的集合中,但它不能同时被<literal>Item</literal>所关联。
</para>
<para>即使三重或多重管理都是可能的:</para>
<programlisting><![CDATA[<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.OrderLine">
<many-to-one name="purchaseDetails" class="eg.Purchase"/>
<many-to-one name="item" class="eg.Item"/>
</composite-element>
</set>
</class>]]></programlisting>
<para>
在查询中,组合元素使用的语法是和关联到其他实体的语法一样的。
</para>
</sect1>
<sect1 id="components-asmapindex">
<title>组件作为Map的索引Components as Map indices </title>
<para>
<literal>&lt;composite-map-key&gt;</literal>元素允许你映射一个Component类作为<literal>Map</literal>的key 但是你必须确定你正确的在这个类中重写了<literal>hashCode()</literal><literal>equals()</literal>方法。
</para>
</sect1>
<sect1 id="components-compositeid" revision="1">
<title>组件作为联合标识符(Components as composite identifiers)</title>
<para>
你可以使用一个component作为一个实体类的标识符。 你的component类必须满足以下要求
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
它必须实现<literal>java.io.Serializable</literal>接口
</para>
</listitem>
<listitem>
<para>
它必须重新实现<literal>equals()</literal><literal>hashCode()</literal>方法, 始终和组合关键字在数据库中的概念保持一致
</para>
</listitem>
</itemizedlist>
<para>
<emphasis>注意在Hibernate3中第二种要求并非是Hibernate强制必须的。但最好这样做。</emphasis>
</para>
<para>
你不能使用一个<literal>IdentifierGenerator</literal>产生组合关键字。作为替代应用程序必须分配它自己的标识符。
</para>
<para>
使用<literal>&lt;composite-id&gt;</literal> 标签(并且内嵌<literal>&lt;key-property&gt;</literal>元素)代替通常的<literal>&lt;id&gt;</literal>标签。 比如,<literal>OrderLine</literal>类具有一个依赖<literal>Order</literal>的(联合)主键的主键。
</para>
<programlisting><![CDATA[<class name="OrderLine">
<composite-id name="id" class="OrderLineId">
<key-property name="lineId"/>
<key-property name="orderId"/>
<key-property name="customerId"/>
</composite-id>
<property name="name"/>
<many-to-one name="order" class="Order"
insert="false" update="false">
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>
....
</class>]]></programlisting>
<para>
现在,任何关联到<literal>OrderLine</literal> 的外键都是复合的。在你的映射文件中,必须为其他类也这样声明。指向<literal>OrderLine</literal>的关联可能被这样映射:
</para>
<programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
<!-- the "class" attribute is optional, as usual -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>]]></programlisting>
<para>
(注意在各个地方<literal>&lt;column&gt;</literal>标签都是<literal>column</literal>属性的替代写法。)
</para>
<para>
指向<literal>OrderLine</literal><literal>多对多</literal>关联也使用联合外键:
</para>
<programlisting><![CDATA[<set name="undeliveredOrderLines">
<key column name="warehouseId"/>
<many-to-many class="OrderLine">
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-many>
</set>]]></programlisting>
<para>
<literal>Order</literal>中, <literal>OrderLine</literal>的集合则是这样:
</para>
<programlisting><![CDATA[<set name="orderLines" inverse="true">
<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set>]]></programlisting>
<para>
(与通常一样,<literal>&lt;one-to-many&gt;</literal>元素不声明任何列.)
</para>
<para>
假若<literal>OrderLine</literal>本身拥有一个集合,它也具有组合外键。
</para>
<programlisting><![CDATA[<class name="OrderLine">
....
....
<list name="deliveryAttempts">
<key> <!-- a collection inherits the composite key type -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</key>
<list-index column="attemptId" base="1"/>
<composite-element class="DeliveryAttempt">
...
</composite-element>
</set>
</class>]]></programlisting>
</sect1>
<sect1 id="components-dynamic" revision="1">
<title>动态组件 Dynamic components</title>
<para>
你甚至可以映射<literal>Map</literal>类型的属性:
</para>
<programlisting><![CDATA[<dynamic-component name="userAttributes">
<property name="foo" column="FOO"/>
<property name="bar" column="BAR"/>
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
</dynamic-component>]]></programlisting>
<para>
<literal>&lt;dynamic-component&gt;</literal>映射的语义上来讲,它和<literal>&lt;component&gt;</literal>是相同的。
这种映射类型的优点在于通过修改映射文件,就可以具有在部署时检测真实属性的能力.利用一个DOM解析器是有可能在运行时刻操作映射文件的。
更好的是,你可以通过<literal>Configuration</literal>对象来访问或者修改Hibernate的运行时元模型。
</para>
</sect1>
</chapter>