6.2. 集合映射( Collection mappings )

HIBERNATE

6.2. 集合映射( Collection mappings )

用于映射集合类的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>
1

name 集合属性的名称

2

table (可选——默认为属性的名称)这个集合表的名称(不能在一对多的关联关系中使用)

3

schema (可选) 表的schema的名称, 他将覆盖在根元素中定义的schema

4

lazy (可选--默认为true) 可以用来关闭延迟加载(false),指定一直使用预先抓取,或者打开"extra-lazy" 抓取,此时大多数操作不会初始化集合类(适用于非常大的集合)

5

inverse (可选——默认为false) 标记这个集合作为双向关联关系中的方向一端。

6

cascade (可选——默认为none) 让操作级联到子实体

7

sort(可选)指定集合的排序顺序, 其可以为自然的(natural)或者给定一个用来比较的类。

8

order-by (可选, 仅用于jdk1.4) 指定表的字段(一个或几个)再加上asc或者desc(可选), 定义Map,Set和Bag的迭代顺序

9

where (可选) 指定任意的SQL where条件, 该条件将在重新载入或者删除这个集合时使用(当集合中的数据仅仅是所有可用数据的一个子集时这个条件非常有用)

10

fetch (可选, 默认为select) 用于在外连接抓取、通过后续select抓取和通过后续subselect抓取之间选择。

11

batch-size (可选, 默认为1) 指定通过延迟加载取得集合实例的批处理块大小("batch size")。

12

access(可选-默认为属性property):Hibernate取得集合属性值时使用的策略

13

乐观锁 (可选 - 默认为 true): 对集合的状态的改变会是否导致其所属的实体的版本增长。 (对一对多关联来说,关闭这个属性常常是有理的)

14

mutable(可变) (可选 - 默认为true): 若值为false,表明集合中的元素不会改变(在某些情况下可以进行一些小的性能优化)。

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开始)。

<map-key 
        column="column_name"
        formula="any SQL expression"
        type="type_name"
        node="@attribute-name"
        length="N"/>
1

column(可选):保存集合索引值的字段名。

2

formula (可选): 用于计算map关键字的SQL公式

3

type (必须):映射键(map key)的类型。

<map-key-many-to-many
        column="column_name"
        formula="any SQL expression"
        class="ClassName"
/>
1

column(可选):集合索引值中外键字段的名称

2

formula (可选): 用于计算map关键字的外键的SQL公式

3

class (必需):映射的键(map key)使用的实体类。

假若你的表没有一个索引字段,当你仍然希望使用List作为属性类型,你应该把此属性映射为Hibernate <bag>。从数据库中获取的时候,bag不维护其顺序,但也可选择性的进行排序。

从集合类可以产生很大一部分映射,覆盖了很多常见的关系模型。我们建议你试验schema生成工具,来体会一下不同的映射声明是如何被翻译为数据库表的。

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"
/>
1

column(可选):保存集合元素值的字段名。

2

formula (可选): 用于计算元素的SQL公式

3

type (必需):集合元素的类型

多对多关联(many-to-many association) 使用 <many-to-many>元素定义.

<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"
    />
1

column(可选): 这个元素的外键关键字段名

2

formula (可选): 用于计算元素外键值的SQL公式.

3

class (必需): 关联类的名称

3

outer-join (可选 - 默认为auto): 在Hibernate系统参数中hibernate.use_outer_join被打开的情况下,该参数用来允许使用outer join来载入此集合的数据。

4

为此关联打开外连接抓取或者后续select抓取。这是特殊情况;对于一个实体及其指向其他实体的多对多关联进全预先抓取(使用一条单独的SELECT),你不仅需要对集合自身打开join,也需要对<many-to-many>这个内嵌元素打开此属性。

5

对外键字段允许DDL生成的时候生成一个惟一约束。这使关联变成了一个高效的一对多关联。(此句存疑:原文为This makes the association multiplicity effectively one to many.)

6

not-found (可选 - 默认为 exception): 指明引用的外键中缺少某些行该如何处理: ignore 会把缺失的行作为一个空引用处理。

7

entity-name (可选): 被关联的类的实体名,作为class的替代。

8

property-ref: (可选) 被关联到此外键(foreign key)的类中的对应属性的名字。若未指定,使用被关联类的主键。

例子:首先, 一组字符串:

<set name="names" table="NAMES">
    <key column="GROUPID"/>
    <element column="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集合的语义:

  • 一个被包含的实体的实例只能被包含在一个集合的实例中

  • 一个被包含的实体的实例只能对应于集合索引的一个值中

一个从ProductPart的关联需要关键字字段,可能还有一个索引字段指向Part所对应的表。 <one-to-many>标记指明了一个一对多的关联。

<one-to-many 
        class="ClassName"
        not-found="ignore|exception"
        entity-name="EntityName"
        node="element-name"
        embed-xml="true|false"
    />
1

class(必须):被关联类的名称。

2

not-found (可选 - 默认为exception): 指明若缓存的标示值关联的行缺失,该如何处理: ignore 会把缺失的行作为一个空关联处理。

3

entity-name (可选): 被关联的类的实体名,作为class的替代。

例子

<set name="bars">
    <key column="foo_id"/>
    <one-to-many class="org.hibernate.Bar"/>
</set>

注意:<one-to-many>元素不需要定义任何字段。 也不需要指定表名。

重要提示:如果一对多关联中的外键字段定义成NOT NULL,你必须把<key>映射声明为not-null="true",或者使用双向关联,并且标明inverse="true"。参阅本章后面关于双向关联的讨论。

下面的例子展示一个Part实体的map,把name作为关键字。( partNamePart的持久化属性)。注意其中的基于公式的索引的用法。

<map name="parts"
        cascade="all">
    <key column="productId" not-null="true"/>
    <map-key formula="partName"/>
    <one-to-many class="Part"/>
</map>