Hibernate.orgCommunity Documentation
组件(Component)这个概念在 Hibernate 中几处不同的地方为了不同的目的被重复使用。
组件(Component)是一个被包含的对象,在持久化的过程中,它被当作值类型,而并非一个实体的引用。在这篇文档中,组件这一术语指的是面向对象的合成概念(而并不是系统构架层次上的组件的概念)。举个例子,你对人(Person)这个概念可以像下面这样来建模:
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;
}
......
......
}
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;
}
}
在持久化的过程中,姓名(Name)
可以作为人(Person)
的一个组件。需要注意的是:你应该为姓名
的持久化属性定义 getter 和 setter 方法,但是你不需要实现任何的接口或申明标识符字段。
以下是这个例子的 Hibernate 映射文件:
<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid"/>
</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
>
人员(Person)表中将包括 pid
,birthday
,initial
,first
和 last
等字段。
就像所有的值类型一样,组件不支持共享引用。换句话说,两个人可能重名,但是两个 Person 对象应该包含两个独立的 Name 对象,只不过这两个 Name 对象具有“同样”的值。组件的值可以为空,其定义如下。 每当 Hibernate 重新加载一个包含组件的对象,如果该组件的所有字段为空,Hibernate 将假定整个组件为空。在大多数情况下,这样假定应该是没有问题的。
组件的属性可以是任意一种 Hibernate 类型(包括集合,多对多关联,以及其它组件等等)。嵌套组件不应该被当作一种特殊的应用(Nested components should not be considered an exotic usage)。Hibernate 倾向于支持细颗粒度的(fine-grained)对象模型。
<component>
元素允许加入一个 <parent>
子元素,在组件类内部就可以有一个指向其容器的实体的反向引用。
<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid"/>
</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
>
Collections of components are supported (e.g. an array of type Name
). Declare your component collection by replacing the <element>
tag with a <composite-element>
tag:
<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
>
注意,如果你定义的 Set 包含组合元素(composite-element),正确地实现 equals()
和 hashCode()
是非常重要的。
组合元素可以包含组件,但是不能包含集合。如果你的组合元素自身包含组件,你必须使用 <nested-composite-element>
标签。这是一个相当特殊的案例 — 在一个组件的集合里,那些组件本身又可以包含其他的组件。这个时候你就应该考虑一下使用 one-to-many 关联是否会更恰当。尝试对这个组合元素重新建模为一个实体 — 但是需要注意的是,虽然 Java 模型和重新建模前是一样的,关系模型和持久性语义会有细微的变化。
请注意如果你使用 <set>
标签,一个组合元素的映射不支持可能为空的属性. 当删除对象时,Hibernate 必须使用每一个字段的值来确定一条记录(在组合元素表中,没有单独的关键字段),如果有为 null 的字段,这样做就不可能了。你必须作出一个选择,要么在组合元素中使用不能为空的属性,要么选择使用 <list>
,<map>
,<bag>
或者 <idbag>
而不是 <set>
。
组合元素有个特别的用法是它可以包含一个<many-to-one>
元素。类似这样的映射允许你将一个 many-to-many 关联表的额外字段映射为组合元素类。接下来的的例子是从 Order
到 Item
的一个多对多的关联关系,关联属性是 purchaseDate
,price
和 quantity
。
<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
>
当然,当你定义 Item 时,你无法引用这些 purchase,因此你无法实现双向关联查询。记住组件是值类型,并且不允许共享引用。某一个特定的 Purchase
可以放在 Order
的集合中,但它不能同时被 Item
所引用。
其实组合元素的这个用法可以扩展到三重或多重关联:
<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
>
在查询中,表达组合元素的语法和关联到其他实体的语法是一样的。
<composite-map-key>
元素允许你映射一个组件类作为一个 Map
的 key,前提是你必须正确的在这个类中重写了 hashCode()
和 equals()
方法。
你可以使用一个组件作为一个实体类的标识符。你的组件类必须满足以下要求:
它必须实现 java.io.Serializable
接口
它必须重新实现 equals()
和 hashCode()
方法,始终和组合关键字在数据库中的概念保持一致
注意:在 Hibernate3 中,第二个要求并非是 Hibernate 强制必须的。但最好这样做。
你不能使用一个 IdentifierGenerator
产生组合关键字。一个应用程序必须分配它自己的标识符。
使用 <composite-id>
标签(并且内嵌 <key-property>
元素)代替通常的 <id>
标签。比如,OrderLine
类具有一个主键,这个主键依赖于 Order
的(联合)主键。
<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
>
现在,任何指向 OrderLine
的外键都是复合的。在你的映射文件中,必须为其他类也这样声明。例如,一个指向 OrderLine
的关联可能被这样映射:
<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
>
The column
element is an alternative to the column
attribute everywhere. Using the column
element just gives more declaration options, which are mostly useful when utilizing hbm2ddl
指向 OrderLine
的多对多
关联也使用联合外键:
<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
>
在 Order
中,OrderLine
的集合则是这样:
<set name="orderLines" inverse="true">
<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set
>
与通常一样,<one-to-many>
元素不声明任何列。
假若 OrderLine
本身拥有一个集合,它也具有组合外键。
<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
>
你甚至可以映射 Map
类型的属性:
<dynamic-component name="userAttributes">
<property name="foo" column="FOO" type="string"/>
<property name="bar" column="BAR" type="integer"/>
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
</dynamic-component
>
从 <dynamic-component>
映射的语义上来讲,它和 <component>
是相同的。这种映射类型的优点在于通过修改映射文件,就可以具有在部署时检测真实属性的能力。利用一个 DOM 解析器,也可以在程序运行时操作映射文件。更好的是,你可以通过 Configuration
对象来访问(或者修改)Hibernate 的运行时元模型。
版权 © 2004 Red Hat, Inc.