6.2. 集合映射( Collection mappings )
提示
从集合类可以产生很大一部分映射,覆盖了很多常见的关系模型。我们建议你试验 schema 生成工具,来体会一下不同的映射声明是如何被翻译为数据库表的。
用于映射集合类的 Hibernate 映射元素取决于接口的类型。比如,
<set>
元素用来映射 Set
类型的属性。
<class name="Product">
<id name="serialNumber" column="productSerialNumber"/>
<set name="parts">
<key column="productSerialNumber" not-null="true"/>
<one-to-many class="Part"/>
</set>
</class
>
除了
<set>
,还有<list>
,<map>
,<bag>
,<array>
和 <primitive-array>
映射元素。<map>
具有代表性:
<map
name="propertyName"
table="table_name"
schema="schema_name"
lazy="true|extra|false"
inverse="true|false"
cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan"
sort="unsorted|natural|comparatorClass"
order-by="column_name asc|desc"
where="arbitrary sql where condition"
fetch="join|select|subselect"
batch-size="N"
access="field|property|ClassName"
optimistic-lock="true|false"
mutable="true|false"
node="element-name|."
embed-xml="true|false"
>
<key .... />
<map-key .... />
<element .... />
</map
>
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
6.2.1. 集合外键(Collection foreign keys)
集合实例在数据库中依靠持有集合的实体的外键加以辨别。此外键作为集合关键字段(collection key column)(或多个字段)加以引用。集合关键字段通过
<key>
元素映射。
在外键字段上可能具有非空约束。对于大多数集合来说,这是隐含的。对单向一对多关联来说,外键字段默认是可以为空的,因此你可能需要指明
not-null="true"
。
<key column="productSerialNumber" not-null="true"/>
外键约束可以使用
ON DELETE CASCADE
。
<key column="productSerialNumber" on-delete="cascade"/>
对
<key>
元素的完整定义,请参阅前面的章节。
6.2.2. 集合元素(Collection elements)
集 合几乎可以包含任何其他的 Hibernate 类型,包括所有的基本类型、自定义类型、组件,当然还有对其他实体的引用。存在一个重要的区别:位于集合中的对象可能是根据“值”语义来操作(其声明周期 完全依赖于集合持有者),或者它可能是指向另一个实体的引用,具有其自己的生命周期。在后者的情况下,被作为集合持有的状态考虑的,只有两个对象之间的 “连接”。
被包容的类型被称为集合元素类型(collection element type)。集合元素通过
<element>
或 <composite-element>
映射,或在其是实体引用的时候,通过 <one-to-many>
或 <many-to-many>
映射。前两种用于使用值语义映射元素,后两种用于映射实体关联。
6.2.3. 索引集合类(Indexed collections)
所有的集合映射,除了 set 和 bag 语义的以外,都需要指定一个集合表的索引字段(index column) — 用于对应到数组索引,或者
List
的索引,或者 Map
的关键字。通过 <map-key>
,Map
的索引可以是任何基础类型;若通过 <map-key-many-to-many>
,它也可以是一个实体引用;若通过 <composite-map-key>
,它还可以是一个组合类型。数组或列表的索引必须是 integer
类型,并且使用 <list-index>
元素定义映射。被映射的字段包含有顺序排列的整数(默认从 0 开始)。
<list-index
column="column_name"
base="0|1|..."/>
| |
|
<map-key
column="column_name"
formula="any SQL expression"
type="type_name"
node="@attribute-name"
length="N"/>
| |
| |
|
<map-key-many-to-many
column="column_name"
formula="any SQL expression"
/>
| |
| |
|
假若你的表没有一个索引字段,当你仍然希望使用
List
作为属性类型,你应该把此属性映射为 Hibernate <bag>。从数据库中获取的时候,bag 不维护其顺序,但也可选择性的进行排序。
6.2.4. 值集合于多对多关联(Collections of values and many-to-many associations)
任何值集合或者多对多关联需要专用的具有一个或多个外键字段的 collection table、一个或多个 collection element column,以及还可能有一个或多个索引字段。
对于一个值集合,我们使用
<element>
标签。例如:
<element
column="column_name"
formula="any SQL expression"
type="typename"
length="L"
precision="P"
scale="S"
not-null="true|false"
unique="true|false"
node="element-name"
/>
| |
| |
|
A many-to-many association is specified using the
<many-to-many>
element.
<many-to-many
column="column_name"
formula="any SQL expression"
class="ClassName"
fetch="select|join"
unique="true|false"
not-found="ignore|exception"
entity-name="EntityName"
property-ref="propertyNameFromAssociatedClass"
node="element-name"
embed-xml="true|false"
/>
| |
| |
| |
| |
| |
| |
| |
|
下面是一些例子:
一系列字符串:
<set name="names" table="person_names">
<key column="person_id"/>
<element column="person_name" type="string"/>
</set
>
包含一组整数的 bag(还设置了
order-by
参数指定了迭代的顺序):
<bag name="sizes"
table="item_sizes"
order-by="size asc">
<key column="item_id"/>
<element column="size" type="integer"/>
</bag
>
一个实体数组,在这个案例中是一个多对多的关联(注意这里的实体是自动管理生命周期的对象(lifecycle objects),
cascade="all"
):
<array name="addresses"
table="PersonAddress"
cascade="persist">
<key column="personId"/>
<list-index column="sortOrder"/>
<many-to-many column="addressId" class="Address"/>
</array
>
一个 map,通过字符串的索引来指明日期:
<map name="holidays"
table="holidays"
schema="dbo"
order-by="hol_name asc">
<key column="id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map
>
一个组件的列表:(将在下一章讨论)
<list name="carComponents"
table="CarComponents">
<key column="carId"/>
<list-index column="sortOrder"/>
<composite-element class="CarComponent">
<property name="price"/>
<property name="type"/>
<property name="serialNumber" column="serialNum"/>
</composite-element>
</list
>
6.2.5. 一对多关联(One-to-many Associations)
一对多关联通过外键连接两个类对应的表,而没有中间集合表。 这个关系模型失去了一些 Java 集合的语义:
一个被包含的实体的实例只能被包含在一个集合的实例中。
一个被包含的实体的实例只能对应于集合索引的一个值中。
一个从
Product
到 Part
的关联需要关键字字段,可能还有一个索引字段指向 Part
所对应的表。<one-to-many>
标记指明了一个一对多的关联。
<one-to-many
class="ClassName"
not-found="ignore|exception"
entity-name="EntityName"
node="element-name"
embed-xml="true|false"
/>
| |
| |
|
注意:
<one-to-many>
元素不需要定义任何字段。也不需要指定表名。
警告
重要提示:如果
一对多
关联中的外键字段定义成 NOT NULL
,你必须把 <key>
映射声明为 not-null="true"
,或者使用双向关联,并且标明 inverse="true"
。参阅本章后面关于双向关联的讨论。
下面的例子展示一个
Part
实体的 map,把 name 作为关键字。( partName
是 Part
的持久化属性)。注意其中的基于公式的索引的用法。
<map name="parts"
cascade="all">
<key column="productId" not-null="true"/>
<map-key formula="partName"/>
<one-to-many class="Part"/>
</map
>