ITEEDU

6.6. 集合排序(Sorted Collections)

Hibernate支持实现java.util.SortedMap和java.util.SortedSet的集合。你必须在映射文件中指定一个比较器:

<set name="aliases" table="person_aliases" sort="natural">
    <key column="person"/>
    <element column="name" type="string"/>
</set>

<map name="holidays" sort="my.custom.HolidayComparator" lazy="true">
    <key column="year_id"/>
    <index column="hol_name" type="string"/>
    <element column="hol_date type="date"/>
</map>

sort属性中允许的值包括unsorted,natural和某个实现了java.util.Comparator的类的名称。

分类集合的行为事实上象java.util.TreeSet或者java.util.TreeMap。

6.7. 对collection排序的其他方法(Other Ways To Sort a Collection)

如果你希望数据库自己对集合元素排序,可以利用set,bag或者map映射中的order-by属性。这个解决方案只能在jdk1.4或者更高的jdk版本中才可以实现(通过LinkedHashSet或者 LinkedHashMap实现)。 它是在SQL查询中完成排序,而不是在内存中。

<set name="aliases" table="person_aliases" order-by="name asc">
    <key column="person"/>
    <element column="name" type="string"/>
</set>

<map name="holidays" order-by="hol_date, hol_name" lazy="true">
    <key column="year_id"/>
    <index column="hol_name" type="string"/>
    <element column="hol_date type="date"/>
</map>

注意: 这个order-by属性的值是一个SQL排序子句而不是HQL的!

关联还可以在运行时使用filter()根据任意的条件来排序。

sortedUsers = s.filter( group.getUsers(), "order by this.name" );

6.8. 垃圾收集(Garbage Collection)

集合是在被持久对象引用时自动持久化,并且不再不引用时自动删除的。 如果集合被从一个持久化对象转移到另外一个, 他的数据可能会被从一个表移到另外一个。 你不需要担心这些。 就跟你通常使用java集合一样使用Hibernate集合即可。

注意:上面的论断在inverse="true"的情况下适用。 我们将在接下来的章节中解释这一点。

6.9. 双向关联(Bidirectional Associations)

双向关联允许通过关联的任一端访问另外一端。在Hibernate中, 支持两种类型的双向关联:

一对多(one-to-many)

Set或者bag值在一端, 单独值(非集合)在另外一端

多对多(many-to-many)

两端都是set或bag值

请注意Hibernate不支持带有索引的集合(list,map或者array)作为"多"的那一端的双向one-to-many关联。

要建立一个双向的多对多关联,只需要映射两个many-to-many关联到同一个数据库表中,并再定义其中的一端为inverse。这里有一个从一个类关联到他自身的many-to-many的双向关联的例子: (原文:You may specify a bidirectional many-to-many association simply by mapping two many-to-many associations to the same database table and declaring one end as inverse. Heres an example of a bidirectional many-to-many association from a class back to itself:)

<class name="eg.Node">
    <id name="id" column="id"/>
    ....
    <bag name="accessibleTo" table="node_access" lazy="true">
        <key column="to_node_id"/>
        <many-to-many class="eg.Node" column="from_node_id"/>
    </bag>
     <!-- inverse end -->
    <bag name="accessibleFrom" table="node_access" inverse="true" lazy="true">
        <key column="from_node_id"/>
        <many-to-many class="eg.Node" column="to_node_id"/>
    </bag>
</class>

如果只对关联的反向端进行了改变,这个改变不会被持久化。 (原文:Changes made only to the inverse end of the association are not persisted.)

要建立一个一对多的双向关联,你可以通过把一个一对多关联,作为一个多对一关联映射到到同一张表的字段上,并且在"多"的那一端定义inverse="true"。 (原文: You may map a bidirectional one-to-many association by mapping a one-to-many association to the same table column(s) as a many-to-one association and declaring the many-valued end inverse="true".)

<class name="eg.Parent">
    <id name="id" column="id"/>
    ....
    <set name="children" inverse="true" lazy="true">
        <key column="parent_id"/>
        <one-to-many class="eg.Child"/>
    </set>
</class>

<class name="eg.Child">
    <id name="id" column="id"/>
    ....
    <many-to-one name="parent" class="eg.Parent" column="parent_id"/>
</class>

在“一”这一端定义inverse="true"不会影响级联操作。 (原文:Mapping one end of an association with inverse="true" doesn't affect the operation of cascades.)

6.10. 三重关联(Ternary Associations)

这里有两种可能的途径来映射一个三重关联。其中一个是使用组合元素(下面将讨论).另外一个是使用一个map,并且带有关联作为其索引。

<map name="contracts" lazy="true">
    <key column="employer_id"/>
    <index-many-to-many column="employee_id" class="Employee"/>
    <one-to-many column="contract_id" class="Contract"/>
</map>
<map name="connections" lazy="true">
    <key column="node1_id"/>
    <index-many-to-many column="node2_id" class="Node"/>
    <many-to-many column="connection_id" class="Connection"/>
</map>

6.11. 异类关联(Heterogeneous Associations)

<many-to-any>和<index-many-to-any>元素提供真正的异类关联。这些元素和<any>元素工作方式是同样的,他们都应该很少用到。

6.12. 集合例子(Collection Example)

在前面的几个章节的确非常令人迷惑。 因此让我们来看一个例子。这个类:

package eg;
import java.util.Set;

public class Parent {
    private long id;
    private Set children;

    public long getId() { return id; }
    private void setId(long id) { this.id=id; }

    private Set getChildren() { return children; }
    private void setChildren(Set children) { this.children=children; }

    ....
    ....
}

这个类有一个eg.Child的实例集合。如果每一个子实例至多有一个父实例, 那么最自然的映射是一个one-to-many的关联关系:

<hibernate-mapping>

    <class name="eg.Parent">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <set name="children" lazy="true">
            <key column="parent_id"/>
            <one-to-many class="eg.Child"/>
        </set>
    </class>

    <class name="eg.Child">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <property name="name"/>
    </class>

</hibernate-mapping>

在以下的表定义中反应了这个映射关系:

create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
alter table child add constraint childfk0 (parent_id) references parent

如果父亲是必须的, 那么就可以使用双向one-to-many的关联了(请看后面父/子关系的章节)。

<hibernate-mapping>

    <class name="eg.Parent">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <set name="children" inverse="true" lazy="true">
            <key column="parent_id"/>
            <one-to-many class="eg.Child"/>
        </set>
    </class>

    <class name="eg.Child">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <property name="name"/>
        <many-to-one name="parent" class="eg.Parent" column="parent_id" not-null="true"/>
    </class>

</hibernate-mapping>

请注意NOT NULL的约束:

create table parent ( id bigint not null primary key )
create table child ( id bigint not null
                     primary key,
                     name varchar(255),
                     parent_id bigint not null )
alter table child add constraint childfk0 (parent_id) references parent

另外一方面,如果一个子实例可能有多个父实例, 那么就应该使用many-to-many关联:

<hibernate-mapping>

    <class name="eg.Parent">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <set name="children" lazy="true" table="childset">
            <key column="parent_id"/>
            <many-to-many class="eg.Child" column="child_id"/>
        </set>
    </class>

    <class name="eg.Child">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <property name="name"/>
    </class>

</hibernate-mapping>

表定义:

create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255) )
create table childset ( parent_id bigint not null, child_id bigint not null, primary key ( parent_id, child_id ) )
alter table childset add constraint childsetfk0 (parent_id) references parent
alter table childset add constraint childsetfk1 (child_id) references child

6.13. <idbag>

如果你完全信奉我们对于“联合主键(composite keys)是个坏东西”,和“实体应该使用(无机的)自己生成的代用标识符(surrogate keys)”的观点,也许你会感到有一些奇怪,我们目前为止展示的多对多关联和值集合都是映射成为带有联合主键的表的!现在,这一点非常值得争辩;看上去一个单纯的关联表并不能从代用标识符中获得什么好处(虽然使用组合值的集合可能会获得一点好处)。不过,Hibernate提供了一个(一点点试验性质的)功能,让你把多对多关联和值集合应得到一个使用代用标识符的表去。

<idbag> 属性让你使用bag语义来映射一个List (或Collection)。

<idbag name="lovers" table="LOVERS" lazy="true">
    <collection-id column="ID" type="long">
        <generator class="hilo"/>
    </collection-id>
    <key column="PERSON1"/>
    <many-to-many column="PERSON2" class="eg.Person" outer-join="true"/>
</idbag>

你可以理解,<idbag>人工的id生成器,就好像是实体类一样!集合的每一行都有一个不同的人造关键字。但是,Hibernate没有提供任何机制来让你取得某个特定行的人造关键字。

注意<idbag>的更新性能要比普通的<bag>高得多!Hibernate可以有效的定位到不同的行,分别进行更新或删除工作,就如同处理一个list, map或者set一样。

在目前的实现中,还不支持使用identity标识符生成器策略。(In the current implementation, the identity identifier generation strategy is not supported.)