最近在研究 Hibernate 的性能优化的时候碰到了"抓取策略", 由于以前没有详细的研究过,

所以到处找资料, 但是无论从一些讲 Hibernate 书籍,还是他人 Blog 中都没有找到详细

介绍 Hibernate 文档中所说的原汁原味的抓取策略, 综合懒加载等等特性混在了一起, 所

以在这自己在借鉴了他人的基础上研究了下原汁原味的 Hibernate 四种"抓取策略";

  • 连 接抓取(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语句获 取一批对象实例或集合

这是文档中的四种抓取策略, 我用 Customer 与 Order 的一个双向一对多例子来使用四种

抓取策略看看他们的不同之处;

    Customer :

  1. public class Customer {
  2. private long id;
  3. private String name;
  4. private Set<Order> orders;
  5. // getter/setter 略
  6. }

    Order :

  1. public class Order {
  2. private long id;
  3. private String name;
  4. private Customer customer;
  5. // getter/setter略
  6. }

Order 的映射文件是不变的, 放在这 :

  1. <hibernate-mapping package="com.purking.strategys.endUpOne">
  2. <class name="Order" table="Order_Table">
  3. <id name="id">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" length="20" column="Order_Name" />
  7. <many-to-one name="customer"
  8. class="Customer"
  9. lazy="proxy"
  10. fetch="select"
  11. column="Cus_ID"
  12. cascade="save-update" />
  13. </class>
  14. </hibernate-mapping>

连 接抓取(Join fetching)

连接抓取, 使用连接抓取可以将原本需要查询两次(或多次)表的多次查询 整合到只需

要一次查询即可完成, 举个例子, 我们在初始化一个含有一对多关系的 Customer 与

Order 的时候, 会先查询 Customer 表,找到需要的 Customer , 然后再根据

Customer.id 到 Order 表中查询将Order 集合初始化, 那么在此完成初始化则需要

发送至少两条 SQL 语句, 而如果使用 join 查询的话, 其会根据需要查询的

Customer.id, 将 Customer 表与 Order 表连接起来进行查询,仅仅一条 SQL 语

句就可以将需要的数据全部查询回来;

使用连接抓取的配置文件 :

  1. <hibernate-mapping package="com.purking.strategys.endUpOne">
  2. <class name="Customer" table="Customer_Table" lazy="true">
  3. <id name="id">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" length="20" column="Cus_Name" />
  7. <set name="orders"
  8. inverse="true"
  9. fetch="join"  //---- Here
  10. <!-- 这里关闭懒加载是为了试验明显 -->
  11. lazy="false">
  12. <key column="Cus_ID" />
  13. <one-to-many class="Order" />
  14. </set>
  15. </class>
  16. </hibernate-mapping>

我们使用如此查询语句 :

  1. Customer c1 = (Customer)session.get(Customer.class, 11l);
  2. c1.getOrders().size();

Hibernate 发出的 SQL 语句为 :

  1. select
  2. customer0_.id as id0_1_,
  3. customer0_.Cus_Name as Cus2_0_1_,
  4. orders1_.Cus_ID as Cus3_3_,
  5. orders1_.id as id3_,
  6. orders1_.id as id1_0_,
  7. orders1_.Order_Name as Order2_1_0_,
  8. orders1_.Cus_ID as Cus3_1_0_
  9. from
  10. Customer_Table customer0_
  11. left outer join
  12. Order_Table orders1_
  13. on customer0_.id=orders1_.Cus_ID
  14. where
  15. customer0_.id=?

在此, Hibernate 使用了 left outer join 连接两个表以一条 SQL 语句将 Order 集合

给初始化了;

查询抓取(Select fetching)

    查询抓取, 这种策略是在集合抓取的时候的默认策略, 即如果集合需要初始化, 那么

会重新发出一条 SQL 语句进行查询; 这是集合默认的抓取策略, 也就是我们常会出现

N+1次查询的查询策略;

配 置文件 :

  1. <hibernate-mapping package="com.purking.strategys.endUpOne">
  2. <class name="Customer" table="Customer_Table" lazy="true">
  3. <id name="id">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" length="20" column="Cus_Name" />
  7. <set name="orders"
  8. inverse="true"
  9. fetch="select">
  10. <key column="Cus_ID" />
  11. <one-to-many class="Order" />
  12. </set>
  13. </class>
  14. </hibernate-mapping>

查询语句不变, 看看 Hibernate 发出的 SQL 语句:

  1. Hibernate:
  2. select
  3. customer0_.id as id0_0_,
  4. customer0_.Cus_Name as Cus2_0_0_
  5. from
  6. Customer_Table customer0_
  7. where
  8. customer0_.id=?
  9. Hibernate:
  10. select
  11. orders0_.Cus_ID as Cus3_1_,
  12. orders0_.id as id1_,
  13. orders0_.id as id1_0_,
  14. orders0_.Order_Name as Order2_1_0_,
  15. orders0_.Cus_ID as Cus3_1_0_
  16. from
  17. Order_Table orders0_
  18. where
  19. orders0_.Cus_ID=?

这就是, 重新发出一条 SQL 语句, 初始化了 Orders 集合;

子查询抓取(Subselect fetching) 

子查询抓取, 另外发送一条SELECT 语句抓取在前面查询到(或者抓取到)的所有实

体对象的关联集合. 这个理解起来有点糊涂, 举个例子 : 如果你使用 Query 查询出了

4 个 Customer 实体, 由于开启了懒加载,那么他们的 Orders 都没有被初始化, 那么我

现在手动初始化一个Customer 的 Orders ,此时由于我选的是 Subselect fetching

策略,所以 Hibernate 会将前面查询到的实体对象(4 个 Customer)的关联集合(在

<set name="orders" fetch="subselect" /> )使用一条 Select 语句一次性抓取

回来, 这样减少了与数据库的交互次数, 一次将每个对象的集合都给初始化了;

[他 是如何这么智能的呢? 原来,他是将上一次查询的 SQL 语句作为这一次查询的 SQL

语句的 where 子查询, 所以上次查询到几个对象,那么这次就初始化几个对象的集

合----- 正因为如此, 所以 subselect 只在 <set> 集合中出现 ];

配置文件:

  1. <hibernate-mapping package="com.purking.strategys.endUpOne">
  2. <class name="Customer" table="Customer_Table" lazy="true">
  3. <id name="id">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" length="20" column="Cus_Name" />
  7. <set name="orders"
  8. inverse="true"
  9. fetch="subselect"
  10. lazy="true">
  11. <key column="Cus_ID" />
  12. <one-to-many class="Order" />
  13. </set>
  14. </class>
  15. </hibernate-mapping>

测试的语句有变化 :

  1. List results = session
  2. .createQuery("From Customer c where c.id in (11,14,17,20)")
  3. .list();
  4. // 这里的四个 id 是我数据库中已经准备好的数据
  5. Customer c0 = (Customer)results.get(0);
  6. c0.getOrders().size();

这个时候再来看看 Hibernate 发出了什么样的 SQL 语句 :

  1. Hibernate:
  2. select
  3. customer0_.id as id0_,
  4. customer0_.Cus_Name as Cus2_0_
  5. from
  6. Customer_Table customer0_
  7. where
  8. customer0_.id in (
  9. 11 , 14 , 17 , 20
  10. )
  11. Hibernate:
  12. select
  13. orders0_.Cus_ID as Cus3_1_,
  14. orders0_.id as id1_,
  15. orders0_.id as id1_0_,
  16. orders0_.Order_Name as Order2_1_0_,
  17. orders0_.Cus_ID as Cus3_1_0_
  18. from
  19. Order_Table orders0_
  20. where
  21. orders0_.Cus_ID in (
  22. select
  23. customer0_.id
  24. from
  25. Customer_Table customer0_
  26. where
  27. customer0_.id in (
  28. 11 , 14 , 17 , 20
  29. )
  30. )

是 不是发出的 SQL 语句形式与这个抓取策略的名字一样? Hibernate 的命名很清晰的;

批量抓取(Batch fetching)

批量抓取:"对查询抓取的 优化方案,通过指定一个主键或外键列表,Hibernate使用

单条SELECT语句获取一批对象实例或集合", 也就是说其本质与 select fetching 是

一样的,只不过将一次一条的 select 策略改为一次 N 条的批量 select 查询; 举个例

子 : 还是借用 Subselect fetching 的例子,我查询出了 4 个 Customer 实体,

Orders 开启了懒加载, 所以我现在来手动初始化一个 Customer 的 orders 属性,

这种策略本质上就是 select fetching,所以如此设置 :

<set name="orders" fetch="select" batch-size="3" /> 那么此时我初始化

一个 Customer 的 orders 集合的时候, Hibernate 还是发出了一条 SQL 语句,

不过这条 SQL 与是通过指定了 Order 表中的 Customer_ID 外键列表(2个), 这个

时候 Hibernate 会以一条 SQL 语句初始化 batch-size 指定的数量的 orders 集合;

[他是如何做到的呢? 通过一个主键或外键 列表 做到的, 他将 4 个 Customer 根据

batch-size 分成了两组, 一组有三个 Customer id 值的列表,第二组只有一个,

在初始化 orders 集合的时候就是根据这两个列表来初始化的]

配置文件 :

  1. <hibernate-mapping package="com.purking.strategys.endUpOne">
  2. <class name="Customer" table="Customer_Table" lazy="true">
  3. <id name="id">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" length="20" column="Cus_Name" />
  7. <set name="orders"
  8. inverse="true"
  9. fetch="select"
  10. lazy="true"
  11. batch-size="3">
  12. <key column="Cus_ID" />
  13. <one-to-many class="Order" />
  14. </set>
  15. </class>
  16. </hibernate-mapping>

在此,我关闭了集合默认的懒加载, 更有利于试验结果测试代码不变,

再来看看 Hibernate 发出的 SQL 语句 :

  1. Hibernate:
  2. select
  3. customer0_.id as id0_,
  4. customer0_.Cus_Name as Cus2_0_
  5. from
  6. Customer_Table customer0_
  7. where
  8. customer0_.id in (
  9. 11 , 14 , 17 , 20
  10. )
  11. Hibernate:
  12. select
  13. orders0_.Cus_ID as Cus3_1_,
  14. orders0_.id as id1_,
  15. orders0_.id as id1_0_,
  16. orders0_.Order_Name as Order2_1_0_,
  17. orders0_.Cus_ID as Cus3_1_0_
  18. from
  19. Order_Table orders0_
  20. where
  21. orders0_.Cus_ID in (
  22. ?, ?, ?
  23. )
  24. Hibernate:
  25. select
  26. orders0_.Cus_ID as Cus3_1_,
  27. orders0_.id as id1_,
  28. orders0_.id as id1_0_,
  29. orders0_.Order_Name as Order2_1_0_,
  30. orders0_.Cus_ID as Cus3_1_0_
  31. from
  32. Order_Table orders0_
  33. where
  34. orders0_.Cus_ID=?

原本需要四次 Select 的查询, 由于 Batch-size=3 只用了两次

就完成了;


总结:

好了, 这里的四种抓取策略说明完了, 来全局看一下, 通过例子可以看出, 这四种抓取

策略并不是所有的情况都合适的, 例如, 如果我需要初始化的是一个单独的实体, 那

么 subselect 对其就没有效果,因为其本身就只需要查询一个对象, 所以 :

  1. Join fetching , Select fetching 与 Batch-size 可以为单个实体的抓取进 
    行性能优化;
  2. Join fetching , Select fetching ,Subselect fetching , Batch fetching 
    都 可以为集合的抓取进行性能优化;

注: 这里对于单个实体可以使用 Batch-size 可能会有点疑惑, 其实在 <class > 上是

具有 Batch-size 抓取策略的; 试想, 使用一个如果是一对一关系呢? 例如 Customer

与 IdCard, 利用 HQL 查询出 4 个 Customer , 我们想一次性初始化 4 个 Customer

的 IdCard 怎么办, 设置 <class name="IdCard" batch-size="4" > , 可能我们

想设置的地方是 <one-to-one batch-size> 但是这里没有提供这个属性, 可能是因为

如果设置了不好理解吧.

【转】Hibernate 原汁原味的四种抓取策略的更多相关文章

  1. Hibernate 原汁原味的四种抓取策略(转)

    原文出处:http://www.cnblogs.com/rongxh7/archive/2010/05/12/1733088.html     尊重原作者,访问原创地址 最近在研究 Hibernate ...

  2. hibernate detached分离查询 与 抓取策略注意事项

    1.detached在抓取策略为 jion显式左外连接查询情况下 会产生笛卡儿积现象 DetachedCriteria dc = DetachedCriteria.forClass(Topic.cla ...

  3. 【Hibernate 8】Hibernate的调优方法:抓取策略

    在上一篇博客中,介绍了Hibernate的缓存机制.合理的配置缓存,可以极大程度上优化Hibernate的性能.这篇博客,介绍另外一个调优方式:抓取策略. 一.什么是抓取策略 抓取策略(fetchin ...

  4. 【Java EE 学习 48】【Hibernate学习第五天】【抓取策略】【二级缓存】【HQL】

    一.抓取策略. 1.hibernate中提供了三种抓取策略. (1)连接抓取(Join Fetch):这种抓取方式是默认的抓取方式.使用这种抓取方式hibernate会在select中内连接的方式获取 ...

  5. Hibernate 抓取策略fetch-1 (select join subselect)

    原文 :http://4045060.blog.51cto.com/4035060/1088025 部分参考:http://www.cnblogs.com/rongxh7/archive/2010/0 ...

  6. 029 hibernate抓取策略

    实例A引用实例B,B如果是代理的话(比如多对一关联中):如果遍历A的查询结果集(假设有10条记录),在遍历A的时候,访问B变量,将会导致n次查询语句的发出!这个时候,如果在B一端的class上配置ba ...

  7. Hibernate fetching strategies(抓取策略)

    抓取策略(fetching strategies)是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候,Hibernate如何获取关联对象的策略.抓取策略可以在O/R映射的 ...

  8. hibernate抓取策略

    抓取策略(fetching strategy) 是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略.抓取策略可以在O/R映射的 ...

  9. Hibernate(十四)抓取策略

    抓取策略: 抓取策略是当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候,Hibernate如何获取关联对象的策略.Hibernate的抓取策略是Hibernate提升性能的一 ...

随机推荐

  1. Andrew Ng机器学习编程作业:Neural Network Learning

    作业文件: machine-learning-ex4 1. 神经网络 在之前的练习中,我们已经实现了神经网络的前反馈传播算法,并且使用这个算法通过作业给的参数值预测了手写体数字.这个练习中,我们将实现 ...

  2. Angular学习笔记—架构简述

    这个架构图展现了 Angular 应用中的 8 个主要构造块: 模块 (module) 组件 (component) 模板 (template) 元数据 (metadata) 数据绑定 (data b ...

  3. Java并发—原子类,java.util.concurrent.atomic包(转载)

    原子类 Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中 的原子操作类提供了一种用法简单.性能高效.线程安全地更新一个变量 ...

  4. java 多线程 day02 定时器

    package com.czbk.thread; import java.util.Date;import java.util.Timer;import java.util.TimerTask; /* ...

  5. df: `/root/.gvfs': Permission denied

    在使用oracle账户检查本地磁盘情况时,总是出现df: `/root/.gvfs': Permission denied信息提示. [oracle@rac1 ~]$ df -h Filesystem ...

  6. Python Parameter Passing Note

    我刚刚开始学习Python, Python中的参数传递总是让我很困惑.我写了4个简单的Demo,帮助我理解Python的参数传递,希望对大家都能有所帮助. 0: def change(x): x = ...

  7. python 系统相关操作

    1.文件 open()代开文件或者创建文件 fout=open('oops.txt','wt') print('Oops, I created a file.',file=fout) fout.clo ...

  8. oracle procedure简单的将临时表的数据插入或更新到目标表

    CREATE OR REPLACE PROCEDURE DEAL_SYNC_SCH_CUSTPHONE_NEW AS CURSOR C_CURU IS SELECT * FROM CBS_COS.SC ...

  9. java实现简单邮件的发送以及常见问题

    java实现简单邮件的发送以及常见问题 最近遇到个需求需要实现发送邮件的功能,以前做发送邮件功能都是有邮箱用户名密码,通过用户名密码连接对应的SMTP服务器来实现邮件的发送.但是这次用公司内部的邮箱, ...

  10. 笔记1:Jmeter工作原理及目录结构

    1.基本工作原理 发送request请求到服务器——获取目标服务的统计信息——生成不同格式的报告 2.完整的工作原理 Jmeter模拟用户并发进行性能测试——发送request到目标服务器——服务器返 ...