Hibernate 原汁原味的四种抓取策略(转)
原文出处:http://www.cnblogs.com/rongxh7/archive/2010/05/12/1733088.html 尊重原作者,访问原创地址
最近在研究 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 :
view plaincopy to clipboardprint?
- public class Customer {
- private long id;
- private String name;
- private Set<Order> orders;
- // getter/setter 略
- }
Order :
view plaincopy to clipboardprint?
- public class Order {
- private long id;
- private String name;
- private Customer customer;
- // getter/setter略
- }
Order 的映射文件是不变的, 放在这 :
view plaincopy to clipboardprint?
- <hibernate-mapping package="com.purking.strategys.endUpOne">
- <class name="Order" table="Order_Table">
- <id name="id">
- <generator class="native" />
- </id>
- <property name="name" length="20" column="Order_Name" />
- <many-to-one name="customer"
- class="Customer"
- lazy="proxy"
- fetch="select"
- column="Cus_ID"
- cascade="save-update" />
- </class>
- </hibernate-mapping>
连接抓取(Join fetching)
连接抓取, 使用连接抓取可以将原本需要查询两次(或多次)表的多次查询 整合到只需
要一次查询即可完成, 举个例子, 我们在初始化一个含有一对多关系的 Customer 与
Order 的时候, 会先查询 Customer 表,找到需要的 Customer , 然后再根据
Customer.id 到 Order 表中查询将Order 集合初始化, 那么在此完成初始化则需要
发送至少两条 SQL 语句, 而如果使用 join 查询的话, 其会根据需要查询的
Customer.id, 将 Customer 表与 Order 表连接起来进行查询,仅仅一条 SQL 语
句就可以将需要的数据全部查询回来;
使用连接抓取的配置文件 :
view plaincopy to clipboardprint?
- <hibernate-mapping package="com.purking.strategys.endUpOne">
- <class name="Customer" table="Customer_Table" lazy="true">
- <id name="id">
- <generator class="native" />
- </id>
- <property name="name" length="20" column="Cus_Name" />
- <set name="orders"
- inverse="true"
- fetch="join" //---- Here
- <!-- 这里关闭懒加载是为了试验明显 -->
- lazy="false">
- <key column="Cus_ID" />
- <one-to-many class="Order" />
- </set>
- </class>
- </hibernate-mapping>
我们使用如此查询语句 :
view plaincopy to clipboardprint?
- Customer c1 = (Customer)session.get(Customer.class, 11l);
- c1.getOrders().size();
Hibernate 发出的 SQL 语句为 :
view plaincopy to clipboardprint?
- select
- customer0_.id as id0_1_,
- customer0_.Cus_Name as Cus2_0_1_,
- orders1_.Cus_ID as Cus3_3_,
- orders1_.id as id3_,
- orders1_.id as id1_0_,
- orders1_.Order_Name as Order2_1_0_,
- orders1_.Cus_ID as Cus3_1_0_
- from
- Customer_Table customer0_
- left outer join
- Order_Table orders1_
- on customer0_.id=orders1_.Cus_ID
- where
- customer0_.id=?
在此, Hibernate 使用了 left outer join 连接两个表以一条 SQL 语句将 Order 集合
给初始化了;
查询抓取(Select fetching)
查询抓取, 这种策略是在集合抓取的时候的默认策略, 即如果集合需要初始化, 那么
会重新发出一条 SQL 语句进行查询; 这是集合默认的抓取策略, 也就是我们常会出现
N+1次查询的查询策略;
配置文件 :
view plaincopy to clipboardprint?
- <hibernate-mapping package="com.purking.strategys.endUpOne">
- <class name="Customer" table="Customer_Table" lazy="true">
- <id name="id">
- <generator class="native" />
- </id>
- <property name="name" length="20" column="Cus_Name" />
- <set name="orders"
- inverse="true"
- fetch="select">
- <key column="Cus_ID" />
- <one-to-many class="Order" />
- </set>
- </class>
- </hibernate-mapping>
查询语句不变, 看看 Hibernate 发出的 SQL 语句:
view plaincopy to clipboardprint?
- Hibernate:
- select
- customer0_.id as id0_0_,
- customer0_.Cus_Name as Cus2_0_0_
- from
- Customer_Table customer0_
- where
- customer0_.id=?
- Hibernate:
- select
- orders0_.Cus_ID as Cus3_1_,
- orders0_.id as id1_,
- orders0_.id as id1_0_,
- orders0_.Order_Name as Order2_1_0_,
- orders0_.Cus_ID as Cus3_1_0_
- from
- Order_Table orders0_
- where
- 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> 集合中出现 ];
配置文件:
view plaincopy to clipboardprint?
- <hibernate-mapping package="com.purking.strategys.endUpOne">
- <class name="Customer" table="Customer_Table" lazy="true">
- <id name="id">
- <generator class="native" />
- </id>
- <property name="name" length="20" column="Cus_Name" />
- <set name="orders"
- inverse="true"
- fetch="subselect"
- lazy="true">
- <key column="Cus_ID" />
- <one-to-many class="Order" />
- </set>
- </class>
- </hibernate-mapping>
测试的语句有变化 :
view plaincopy to clipboardprint?
- List results = session
- .createQuery("From Customer c where c.id in (11,14,17,20)")
- .list();
- // 这里的四个 id 是我数据库中已经准备好的数据
- Customer c0 = (Customer)results.get(0);
- c0.getOrders().size();
这个时候再来看看 Hibernate 发出了什么样的 SQL 语句 :
view plaincopy to clipboardprint?
- Hibernate:
- select
- customer0_.id as id0_,
- customer0_.Cus_Name as Cus2_0_
- from
- Customer_Table customer0_
- where
- customer0_.id in (
- 11 , 14 , 17 , 20
- )
- Hibernate:
- select
- orders0_.Cus_ID as Cus3_1_,
- orders0_.id as id1_,
- orders0_.id as id1_0_,
- orders0_.Order_Name as Order2_1_0_,
- orders0_.Cus_ID as Cus3_1_0_
- from
- Order_Table orders0_
- where
- orders0_.Cus_ID in (
- select
- customer0_.id
- from
- Customer_Table customer0_
- where
- customer0_.id in (
- 11 , 14 , 17 , 20
- )
- )
是不是发出的 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 集合的时候就是根据这两个列表来初始化的]
配置文件 :
view plaincopy to clipboardprint?
- <hibernate-mapping package="com.purking.strategys.endUpOne">
- <class name="Customer" table="Customer_Table" lazy="true">
- <id name="id">
- <generator class="native" />
- </id>
- <property name="name" length="20" column="Cus_Name" />
- <set name="orders"
- inverse="true"
- fetch="select"
- lazy="true"
- batch-size="3">
- <key column="Cus_ID" />
- <one-to-many class="Order" />
- </set>
- </class>
- </hibernate-mapping>
在此,我关闭了集合默认的懒加载, 更有利于试验结果测试代码不变,
再来看看 Hibernate 发出的 SQL 语句 :
view plaincopy to clipboardprint?
- Hibernate:
- select
- customer0_.id as id0_,
- customer0_.Cus_Name as Cus2_0_
- from
- Customer_Table customer0_
- where
- customer0_.id in (
- 11 , 14 , 17 , 20
- )
- Hibernate:
- select
- orders0_.Cus_ID as Cus3_1_,
- orders0_.id as id1_,
- orders0_.id as id1_0_,
- orders0_.Order_Name as Order2_1_0_,
- orders0_.Cus_ID as Cus3_1_0_
- from
- Order_Table orders0_
- where
- orders0_.Cus_ID in (
- ?, ?, ?
- )
- Hibernate:
- select
- orders0_.Cus_ID as Cus3_1_,
- orders0_.id as id1_,
- orders0_.id as id1_0_,
- orders0_.Order_Name as Order2_1_0_,
- orders0_.Cus_ID as Cus3_1_0_
- from
- Order_Table orders0_
- where
- orders0_.Cus_ID=?
原本需要四次 Select 的查询, 由于 Batch-size=3 只用了两次
就完成了;
总结:
好了, 这里的四种抓取策略说明完了, 来全局看一下, 通过例子可以看出, 这四种抓取
策略并不是所有的情况都合适的, 例如, 如果我需要初始化的是一个单独的实体, 那
么 subselect 对其就没有效果,因为其本身就只需要查询一个对象, 所以 :
- Join fetching , Select fetching 与 Batch-size 可以为单个实体的抓取进
行性能优化; - 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 原汁原味的四种抓取策略(转)的更多相关文章
- 【转】Hibernate 原汁原味的四种抓取策略
最近在研究 Hibernate 的性能优化的时候碰到了"抓取策略", 由于以前没有详细的研究过, 所以到处找资料, 但是无论从一些讲 Hibernate 书籍,还是他人 Blog ...
- hibernate detached分离查询 与 抓取策略注意事项
1.detached在抓取策略为 jion显式左外连接查询情况下 会产生笛卡儿积现象 DetachedCriteria dc = DetachedCriteria.forClass(Topic.cla ...
- 【Hibernate 8】Hibernate的调优方法:抓取策略
在上一篇博客中,介绍了Hibernate的缓存机制.合理的配置缓存,可以极大程度上优化Hibernate的性能.这篇博客,介绍另外一个调优方式:抓取策略. 一.什么是抓取策略 抓取策略(fetchin ...
- 【Java EE 学习 48】【Hibernate学习第五天】【抓取策略】【二级缓存】【HQL】
一.抓取策略. 1.hibernate中提供了三种抓取策略. (1)连接抓取(Join Fetch):这种抓取方式是默认的抓取方式.使用这种抓取方式hibernate会在select中内连接的方式获取 ...
- Hibernate 抓取策略fetch-1 (select join subselect)
原文 :http://4045060.blog.51cto.com/4035060/1088025 部分参考:http://www.cnblogs.com/rongxh7/archive/2010/0 ...
- 029 hibernate抓取策略
实例A引用实例B,B如果是代理的话(比如多对一关联中):如果遍历A的查询结果集(假设有10条记录),在遍历A的时候,访问B变量,将会导致n次查询语句的发出!这个时候,如果在B一端的class上配置ba ...
- Hibernate fetching strategies(抓取策略)
抓取策略(fetching strategies)是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候,Hibernate如何获取关联对象的策略.抓取策略可以在O/R映射的 ...
- hibernate抓取策略
抓取策略(fetching strategy) 是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略.抓取策略可以在O/R映射的 ...
- Hibernate(十四)抓取策略
抓取策略: 抓取策略是当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候,Hibernate如何获取关联对象的策略.Hibernate的抓取策略是Hibernate提升性能的一 ...
随机推荐
- nginx 常见参数以及重定向参数配置
nginx 各参数翻译,作用 $arg_PARAMETER #这个变量包含GET请求中,如果有变量PARAMETER时的值. $args #这个变量等于请求行中(GET请求)的参数,例如foo=123 ...
- centos7设置ip
centos7不能再通过setup命令来设置ip了,但可以通过修改网卡配置文件来设置ip 在/etc/sysconfig/network-scripts目录下找到网卡配置文件,修改之前内容如下 TYP ...
- 题外话:计算密集型 vs IO密集型
我们把任务分为计算密集型和IO密集型,erlang作为IO密集型的语言,适合网关等相关的场景,而对计算达到某一量级后,可能处理效率下降的很明显. erlang不适合数值计算.erlang是解释型的,虽 ...
- android 二维码制作,显示到UI,并保存SD卡,拿来就能用!!
转载请注明出处:王亟亟的大牛之路 如今二维码已经渗透了我们的生活.各种扫码关注啊.扫码下载的,今天上一个依据输入内容生成二维码的功能. 包结构: 界面截图: 功能:输入网址–>生成图片–> ...
- hdu 4414 Finding crosses【简单模拟】
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4414 CSUST:点击打开链接 Finding crosses Time Limit: 2000/1000 ...
- phpstorm10激活方法
选择 license server ---> http://idea.lanyus.com/ (末尾的斜杠不能漏了!) 2016-7-5更新 上面的注册方法再试时,已被封杀 2017-9-1 ...
- 洛谷P2402 奶牛隐藏
洛谷P2402 奶牛隐藏 题目背景 这本是一个非常简单的问题,然而奶牛们由于下雨已经非常混乱,无法完成这一计算,于是这个任务就交给了你.(奶牛混乱的原因看题目描述) 题目描述 在一个农场里有n块田地. ...
- c++得到本地username和IP
bool CDlgResetAlarmInfo::GetLocalUserNameAddIP(CString &a_lstrUserName ,CString &a_IpStr) { ...
- Onenet GPS上传经纬度的格式 笔记
首先搞清楚几个问题: 1.GPS输出的经纬度 GPS获取的数据: 3438.1633,N,11224.4992,E 格式是ddmm.mmmmm 2.Onenet服务器识别的经纬度坐标格式 服务器识别需 ...
- 海信电视 LED55K370 升级固件总结【含固件下载地址】
最早电视买回来,感觉垃圾软件太多,root后,删软件不小心删除了桌面,导致没桌面. 用ADB装了点软件,凑合可以用. 后来装了悟空遥控,然后装了沙发桌面,不影响使用了. 最近海信不停推送更新系统,改手 ...