Hibernate 定义了三种基本类型的集合:
-
值数据集合
-
一对多关联(One-to-many Associations)
-
多对多关联
这个分类是区分了不同的表和外键关系类型,但是它没有告诉我们关系模型的所有内容。 要完全理解他们的关系结构和性能特点,我们必须同时考虑“用于 Hibernate 更新或删除集合行数据的主键的结构”。因此得到了如下的分类:
-
有序集合类
-
集合(sets)
-
包(bags)
所有的有序集合类(maps,lists,arrays)都拥有一个由
<key>
和 <index>
组成的主键。这种情况下集合类的更新是非常高效的
— 主键已经被有效的索引,因此当 Hibernate 试图更新或删除一行时,可以迅速找到该行数据。
集合(sets)的主键由
<key>
和其他元素字段构成。对于有些元素类型来说,这很低效,特别是组合元素或者大文本、大二进制字段;数据库可能无法有效的对复杂的主键进行索引。另一方面,对于一对多、多对多关联,特别是合成的标识符来说,集合也可以达到同样的高效性能。(
附注:如果你希望 SchemaExport
为你的 <set>
创建主键,你必须把所有的字段都声明为 not-null="true"
。)
<idbag>
映射定义了代理键,因此它总是可以很高效的被更新。事实上,<idbag>
拥有着最好的性能表现。
Bag 是最差的。因为 bag 允许重复的元素值,也没有索引字段,因此不可能定义主键。 Hibernate 无法判断出重复的行。当这种集合被更改时,Hibernate 将会先完整地移除 (通过一个(in a single
DELETE
))整个集合,然后再重新创建整个集合。因此
Bag 是非常低效的。
请注意:对于一对多关联来说,“主键”很可能并不是数据库表的物理主键。但就算在此情况下,上面的分类仍然是有用的。(它仍然反映了 Hibernate 在集合的各数据行中是如何进行“定位”的。)
Lists,maps 和 sets 用于更新效率最高
根据我们上面的讨论,显然有序集合类型和大多数 set 都可以在增加、删除、修改元素中拥有最好的性能。
可论证的是对于多对多关联、值数据集合而言,有序集合类比集合(set)有一个好处。因为
Set
的内在结构,
如果“改变”了一个元素,Hibernate 并不会更新(UPDATE)
这一行。对于 Set
来说,只有在插入(INSERT)
和删除(DELETE)
操作时“改变”才有效。再次强调:这段讨论对“一对多关联”并不适用。
注意到数组无法延迟载入,我们可以得出结论,list,map 和 idbags 是最高效的(非反向)集合类型,set 则紧随其后。 在 Hibernate 中,set 应该时最通用的集合类型,这时因为“set”的语义在关系模型中是最自然的。
但是,在设计良好的 Hibernate 领域模型中,我们通常可以看到更多的集合事实上是带有
inverse="true"
的一对多的关联。对于这些关联,更新操作将会在多对一的这一端进行处理。因此对于此类情况,无需考虑其集合的更新性能。
Bag 和 list 是反向集合类中效率最高
在把 bag 扔进水沟之前,你必须了解,在一种情况下,bag 的性能(包括list)要比 set 高得多:对于指明了
inverse="true"
的集合类(比如说,标准的双向的一对多关联),我们可以在未初始化(fetch)包元素的情况下直接向
bag 或 list 添加新元素! 这是因为 Collection.add()
)或者 Collection.addAll()
方法对
bag 或者 List 总是返回 true(这点与与 Set 不同)。因此对于下面的相同代码来说,速度会快得多。
Parent p = (Parent) sess.load(Parent.class, id); Child c = new Child(); c.setParent(p); p.getChildren().add(c); //no need to fetch the collection! sess.flush();