最近在研究 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 plain
copy to clipboard
print
?
-
public class Customer {
-
private long id;
-
private String name;
-
private Set<Order> orders;
-
// getter/setter 略
-
}
Order :
view plain
copy to clipboard
print
?
-
public class Order {
-
private long id;
-
private String name;
-
private Customer customer;
-
// getter/setter略
-
}
Order 的映射文件是不变的, 放在这 :
view plain
copy to clipboard
print
?
-
<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 plain
copy to clipboard
print
?
-
<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 plain
copy to clipboard
print
?
-
Customer c1 = (Customer)session.get(Customer.class, 11l);
-
c1.getOrders().size();
Hibernate 发出的 SQL 语句为 :
view plain
copy to clipboard
print
?
-
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 plain
copy to clipboard
print
?
-
<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 plain
copy to clipboard
print
?
-
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 plain
copy to clipboard
print
?
-
<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 plain
copy to clipboard
print
?
-
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 plain
copy to clipboard
print
?
-
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 plain
copy to clipboard
print
?
-
<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 plain
copy to clipboard
print
?
-
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> 但是这里没有提供这个属性, 可能是因为
如果设置了不好理解吧..
http://www.cnblogs.com/rongxh7/archive/2010/05/12/1733088.html
分享到:
相关推荐
为了解决这个问题,Hibernate提供了几种抓取策略,包括批量抓取和预加载。 批量抓取允许我们在一次查询中获取多个关联对象,而不是逐个加载。例如,如果我们有一个`User`实体,每个用户都有多个`Order`,我们可以...
今天我们要探讨的是Hibernate的抓取策略,特别是针对set集合的策略。在大型应用中,有效地管理数据加载能显著提升性能,减少数据库交互次数,这就是抓取策略的核心作用。 首先,我们需要理解什么是抓取策略。在...
今天我们要讨论的是Hibernate框架中的一个关键概念——抓取策略,特别是针对"many-to-one"关系的抓取策略。这篇博客将深入剖析这个主题,帮助你更好地理解和应用Hibernate。 Hibernate是Java领域中最流行的对象关系...
在Java的持久化框架Hibernate中,数据访问优化是至关重要的,而抓取策略(Fetch Strategy)和懒加载(Lazy Loading)则是实现这一目标的关键技术。本文将深入探讨这两个概念,并通过具体的案例进行分析。 首先,让...
子查询抓取策略为集合代理提供了一种特殊的抓取方式,它不使用外连接,而是为每个实体对象单独发送一条SELECT语句来抓取关联集合。这种方式在某些情况下能更精细地控制数据加载,但可能导致较多的数据库交互。 ### ...
hibernate3数据检索、抓取策略 、 注解的使用
它可以分为立即抓取、延迟集合抓取、代理抓取和属性延迟加载四种类型,每种都有其适用场景。 延迟加载(Lazy loading)是Hibernate的重要特性,它默认对集合使用延迟SELECT抓取,对单值关联使用延迟代理抓取。但...
本篇文章主要探讨了Hibernate中的多表查询和抓取策略,这对于理解如何高效地从数据库获取数据至关重要。 1. **Hibernate中的多表查询** Hibernate支持多种多表查询方式,类似于SQL中的连接查询: - **交叉连接*...
Hibernate 简介 Hibernate 开发流程 Hibernate 配置文件 Hibernate 核心接口和类 Hibernate ORM映射 HQL Hibernate 懒加载机制与抓取策略 Hibernate 缓存 Hibernate 锁机制
Hibernate支持以下四种事务隔离级别: 1. **读未提交**(Read Uncommitted):最低的隔离级别,允许读取尚未提交的数据变更。 2. **读已提交**(Read Committed):允许一个事务只能看见已经提交事务所做的改变。 3...
你将直接深入到Hibernate的富编程模型之中,贯穿映射、查询、抓取策略、事务、会话、缓存以及更多其他内容。书中图文并茂地介绍了数据库设计和优化技术的实践。在本书中,作者详尽介绍了具有Java持久化2.1标准的...
在Hibernate Day 04的学习中,我们深入探讨了如何利用Hibernate进行查询操作以及它的抓取策略,这些是理解并有效使用Hibernate的关键部分。 首先,让我们来详细了解一下Hibernate的查询方式。Hibernate提供了一个...
包括基础抓取知识和应用抓取策略,例如通过查询和配置文件动态抓取数据。 批处理:Hibernate支持JDBC批处理,以提高批量数据操作的性能。 缓冲:讲解了如何配置二级缓冲以及管理缓冲数据。 拦截器和事件:这部分...
- 抓取策略包括单个对象、集合的懒加载和Eager加载等,影响数据的获取时机。 11. **事务隔离级别** - 事务的隔离级别包括读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ...
抓取策略(Fetching)介绍了Hibernate如何抓取对象及其关联对象,包括延迟加载和立即加载的策略选择。 批量操作(Batching)涉及如何执行数据库批处理操作,这对于执行大量数据更新或插入非常有用。 缓存机制...
9. Fetching(抓取)章节,说明了如何配置和应用不同的抓取策略来优化数据库访问和提升性能。包括不抓取、通过查询动态抓取、通过配置文件动态抓取等策略。 10. 批处理章节,展示了如何通过JDBC批处理来执行批量...
以上内容是Hibernate5用户手册中文版的一部分知识点,涵盖了从基础的体系架构介绍到复杂的数据抓取策略和事务控制管理。手册为开发者提供了使用Hibernate进行数据持久化的全面指导,帮助开发者更好地理解和应用该...
这些配置属性包括SQL方言、外连接抓取、二进制流、二级和查询缓存、查询语言替代、Hibernate统计信息和日志系统。此外,文档还提供了如何实现命名策略和持久化类提供者的指导。文档最后提及了Hibernate的XML配置文件...