关于Hibernate中对集合类型的映射

本文深入探讨了Hibernate中集合映射的多种应用场景,包括@JoinColumn、Set、List和Bag的使用方法,以及如何在映射中指定排序方式以生成有序集合,如HashSet、LinkedHashSet和TreeSet。此外,文章还介绍了LazyCollection选项如何优化集合的懒加载性能,避免加载大量数据时的性能瓶颈。

Each interface has a matching implementation
supported by Hibernate, and it’s important that you use the right combination.

先说一个 @JoinColumn的问题。

这个注解是用来说明关联列的信息的。

按自然的做法是:多在Many端上描述这些信息,因为从DB表上来看,关联列者是many端的表上外键列。

但是,如果是单向的one-to-many时就只能在one上映射了。这时候看起来会有些让人迷惑。

再补补集合中的一些知识吧,有些确实生疏了:

关于Set下面的几个具体实际的对比:

  1. *HashSet:哈希表是通过使用称为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放;
  2. *LinkedHashSet:以元素插入的顺序来维护集合的链接表,允许以插入的顺序在集合中迭代;
  3. *TreeSet:提供一个使用树结构存储Set接口的实现,通过提供元素比较器,使得集合在插入过程中直接有序。

LinkedHashSet没有实现什么特殊接口用以表明它是有序的。

1.关于Set的映射

在基于xml配制的情况下如果你的只是声明一个普通的set,那个在实例化对象时,hibernate会生成HashSet。

而如果在set的配制中加入order-by,hibernate就会生成LinkedHashSet了!这样就是一个有序的集合了。

关于Set的排序和Hibernate如何生成Set实例的问题:

内存排序:

Set映射有两种排序方式,一是使用映射文件中的sort属性,一般需要自己实现一个java.util.Comparator,sort属性

指定自己实现的比较类,hibernate返回给客户的实际是Set的TreeSet实现,将该比较类作为treeSet的比较器,这种

排序是在内存中进行的,可以在比较器中按实体类的某个字段排序或实现更复杂的排序方法,非常灵活,但是要自己实

现比较器,麻烦一些。

以下是两个例子:

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

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

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

数据库排序:

另一种方法是使用映射中的order-by属性,可以指定表中的一个排序字段,排序是在数据库中进行的,hibernate返回

LinkedHashSet实现,可以保持对象的前后次序。

以下又是两个例子

<set name="aliases" table="person_aliases" order-by="lower(name) asc" >
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" order-by="hol_date, hol_name" >
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date type="date"/>
</map>


所以参考中说在实体类中定义子集合时不要定义成HashSet,而应该是Set接口,因为它返回的不一定是HashSet。

对于List(Bag映射),可以指定order-by排序字段,并不需要index列。

关于List的影射:

首先要强调的是:很多时候,我们的java.util.List可以映射成bag的!

因为大多数时候,我们需要的只是一组从数据库里取出的数据,至于顺序,只能是从数据库中查出时的先后顺序(按ID升序)。在这种需求下,如果要使用set,那么必须要在映射中加入order-by,而如果使用list(xml中的list),那么还需要额外的index-column!所以最好的方案就是用bag.

在基于注解的配制方式时:

如果在List字段上不加@IndexColumn,那么这个List就会被映射成Hibernate里bag

1、其实hibernate是有提供list映射的, list就是bag类型,bag适合关联的集合类中有排序需求

在配置list的时候,也有几种方法:指定list-index,这也就要在数据库中对应创建一个字段表示顺序

对于一对多关联当中的List,需要在数据库里面维护一个index列,如果List当中的某个元素被删除,那么Hibernate

连续发送多条update语句,更新后续所有元素的index列,以确保index的连续性(在inverse为false的情况下),如果

你选择自己维护index列,也同样会面临这个问题,甚至更棘手(在inverse为true的情况下),所以List被谨慎的使用在

极其罕见的场合。

但是,这里所说的要特别慎用list是指的hibernate映射文件中的list类型,而不是实体类中的List类型。映射文件中

用Bag类型,在实体类中是可以对应List的。

关于@CollectionOfElements

Hibernate一直强调值对象与实体类之间差异,这在集合类型的映射上体现的极为明显。在映射集合类型的值对象进,使用xml有<element>, 使用注解则有@CollectionOfElements

关于@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)

The collection is no longer initialized if you call size(), contains(), or isEmpty().

org.hibernate.annotations.LazyCollectionOption.EXTRA是较一般lazy loading更加lazy,或者说更加智能的选项,当访问集合的size(), contains(), or isEmpty()时,并不加载整个集合,这对加载大集合来说很适用!

使用了@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA),
集合变得更“聪明”,调用size()时,不会加载集合,打出的sql是:

select
count(id)
from
Thread
where
forumId =?

没有使用@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)时,
只要用的size(),就会导致整个集合加载!以是打出的SQL
select
threads0_.forumId as forumId4_,
threads0_.id as id4_,
threads0_.id as id5_3_,
threads0_.creationTime as creation2_5_3_,
threads0_.forumId as forumId5_3_,
threads0_.modifiedTime as modified3_5_3_,
threads0_1_.subjectId as subjectId6_3_,
post1_.id as id4_0_,
post1_.authorId as authorId4_0_,
post1_.creationTime as creation2_4_0_,
post1_.isSubject as isSubject4_0_,
post1_.messageBody as messageB4_4_0_,
post1_.modifiedTime as modified5_4_0_,
post1_.quotedPostId as quotedPo8_4_0_,
post1_.title as title4_0_,
user2_.id as id0_1_,
user2_.accountNonExpired as accountN2_0_1_,
user2_.accountNonLocked as accountN3_0_1_,
user2_.credentialsNonExpired as credenti4_0_1_,
user2_.email as email0_1_,
user2_.enabled as enabled0_1_,
user2_.password as password0_1_,
user2_.username as username0_1_,
user2_.version as version0_1_,
post3_.id as id4_2_,
post3_.authorId as authorId4_2_,
post3_.creationTime as creation2_4_2_,
post3_.isSubject as isSubject4_2_,
post3_.messageBody as messageB4_4_2_,
post3_.modifiedTime as modified5_4_2_,
post3_.quotedPostId as quotedPo8_4_2_,
post3_.title as title4_2_
from
Thread threads0_
left outer join
Thread_Subject threads0_1_
on threads0_.id=threads0_1_.threadId
left outer join
Post post1_
on threads0_1_.subjectId=post1_.id
left outer join
User user2_
on post1_.authorId=user2_.id
left outer join
Post post3_
on post1_.quotedPostId=post3_.id
where
threads0_.forumId=?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值