hibernate 抓取策略

hibernate 抓取策略(Fetching strategies)
抓取策略(fetching strategy) 是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略。抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL 或条件查询(Criteria Query)中重载声明。

Hibernate3 定义了如下几种抓取策略:

连接抓取(Join fetching) - Hibernate通过 在SELECT语句使用OUTER JOIN(外连接)来 获得对象的关联实例或者关联集合。

查询抓取(Select fetching) - 另外发送一条 SELECT 语句抓取当前对象的关联实体或集合。除非你显式的指定lazy="false"禁止 延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。

子查询抓取(Subselect fetching) - 另外发送一条SELECT 语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合。除非你显式的指定lazy="false" 禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。

批量抓取(Batch fetching) - 对查询抓取的优化方案, 通过指定一个主键或外键列表,Hibernate使用单条SELECT语句获取一批对象实例或集合。

Hibernate会区分下列各种情况:

Immediate fetching,立即抓取 - 当宿主被加载时,关联、集合或属性被立即抓取。

Lazy collection fetching,延迟集合抓取- 直到应用程序对集合进行了一次操作时,集合才被抓取。(对集合而言这是默认行为。)

"Extra-lazy" collection fetching,"Extra-lazy"集合抓取 -对集合类中的每个元素而言,都是直到需要时才去访问数据库。除非绝对必要,Hibernate不会试图去把整个集合都抓取到内存里来(适用于非常大的集合)。

Proxy fetching,代理抓取 - 对返回单值的关联而言,当其某个方法被调用,而非对其关键字进行get操作时才抓取。

"No-proxy" fetching,非代理抓取 - 对返回单值的关联而言,当实例变量被访问的时候进行抓取。与上面的代理抓取相比,这种方法没有那么“延迟”得厉害(就算只访问标识符,也会导致关联抓取)但是更加透明,因为对应用程序来说,不再看到proxy。这种方法需要在编译期间进行字节码增强操作,因此很少需要用到。

Lazy attribute fetching,属性延迟加载 - 对属性或返回单值的关联而言,当其实例变量被访问的时候进行抓取。需要编译期字节码强化,因此这一方法很少是必要的。

这里有两个正交的概念:关联何时被抓取,以及被如何抓取(会采用什么样的SQL语句)。不要混淆它们!我们使用抓取来改善性能。我们使用延迟来定义一些契约,对某特定类的某个脱管的实例,知道有哪些数据是可以使用的。 
 
----------------------------------------------------
---------------------------
一个抓取的例子:

例子:ForumGroup和Forum

 

[c-sharp] view plain copy print ?
  1. public class ForumGroup {  
  2.  @OneToMany(fetch=FetchType.EAGER,mappedBy="group")  
  3.  private List<Forum> forums = new ArrayList<Forum>();  
  4. }  
  5. public class Forum {  
  6.  @ManyToOne(fetch=FetchType.LAZY)  
  7.  @JoinColumn(name="groupId",nullable=false)  
  8.  @org.hibernate.annotations.ForeignKey(name="fk_Forum_groupId_ForumGroup")  
  9.  private ForumGroup group;  
  10. }  

由于ForumGroup的forums被配制为eager fetch,也就是说在加载ForumGroup时,会同一并把它的所有Forum一起加载出来,这个过程是通过left out join来实现的。下面是hibernate打出的sql.

select        this_.id as id3_1_,        this_.creationTime as creation2_3_1_,        this_.description as descript3_3_1_,        this_.modifiedTime as modified4_3_1_,        this_.name as name3_1_,        forums2_.groupId as groupId3_,        forums2_.id as id3_,        forums2_.id as id2_0_,        forums2_.creationTime as creation2_2_0_,        forums2_.description as descript3_2_0_,        forums2_.groupId as groupId2_0_,        forums2_.modifiedTime as modified4_2_0_,        forums2_.name as name2_0_     from        ForumGroup this_     left outer join        Forum forums2_             on this_.id=forums2_.groupId

假定有1个ForumGroup,它有3个Forum,上面的sql会生成3条记录。那么Hibernate是如何封装这3条数据的呢?从实际的代码运行结果中可以看到:首先,Hibernate确实只生成了一个ForumGroup的实例和3个Forum的实例。但是如果你使用loadAllForumGroup时,会返回3个ForumGroup的引用,指向同一个ForumGroup的实例!

如果把ForumGroup的forums被配制为lazy fetch,会解决这个问题。但这也只是回避了这个问题!

正确的解决方法是什么呢?

 如果想过滤join和fetch中的重复对象,有两种方法:

1.将续集里封装成set.比如:Set noDupes = new LinkedHashSet(resultList))

2.使用DISTINCT。比如:select distinct i from Item i join fetch i.bids

 

另外一个问题是:

在用JPA进行注释时,如果一个实体里要映射多个集合实体时,我们不能把两个集合的的FetchType设置为EAGER,此时只能设置为LAZY,否则会报:cannot simultaneously fetch multiple bags。或者我们也可以借助:@IndexColumn (加了它就不再是bag集合了,而是list集合了)。也就是说,hibernate不允许一次抓取多个bag.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值