延迟加载:控制sql语句发送时机

抓取策略:控制sql语句格式,子查询、连接查询、普通sql

延迟加载

延迟加载(lazy),也叫做懒加载;执行到该行代码时,不发送sql进行查询,只有在真正使用到这个对象的一些未知的属性才会真正发送sql去查询

分类:

  • 类级别的延迟加载
  • 关联级别的延迟加载

类级别的延迟加载

  • 使用load方法查询某个对象时是否使用延迟加载
  • 一般在映射文件的<class>标签中配置lazy属性:<class name="com.qf.entity.Parent" table="parent" lazy="false">
    • 不采用延迟:lazy="false"
    • 采用延迟:lazy="true"
  • 类级别延迟加载失效方法
    • <class>标签中配置lazy="false"
    • 使用final修饰持久化类(延迟加载内部使用javassist技术实现,需要继承持久化类,但是final类无法被继承)
    • Hibernate.initialize(obj):执行该方法会立刻发送sql进行查询

测试

映射文件:Parent.hbm.xml

<hibernate-mapping>
<class name="com.qf.entity.Parent" table="parent" lazy="true">
<id name="pid" column="pid">
<generator class="native"/>
</id>
<property name="pname" column="pname"/>
<property name="age" column="age"/>
<set name="childs" cascade="save-update">
<key column="pid"/>
<one-to-many class="com.qf.entity.Children" />
</set>
</class>
</hibernate-mapping>

测试方法

@Test
/**
* HQL简单查询
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction(); Parent parent = session.load(Parent.class, 1L);//代码执行到这里并没有发送sql System.out.println(parent);//在真正使用的时候发送sql执行查询。此处发送sql tx.commit();
}

关联级别的延迟加载

指的是查询到某个对象时,查询其关联的对象,需不需要延迟

在映射文件的<set>标签或者<many-to-one>标签中配置

<set name="childs" cascade="save-update" lazy="true">
----------------------------------------
<many-to-one lazy="false" name="p" class="com.qf.entity.Parent" column="pid" />
  • set标签中的lazy的属性值可选

    • lazy="true"  :默认值,查询关联对象时,采用延迟加载
    • lazy="false"  :查询关联对象时,不采用延迟加载
    • lazy="extra"  :极其懒惰的延迟加载(用到谁查谁)
  • many-to-one标签中的lazy属性值可选
    • lazy="false"             :查询关联对象时,不采用延迟加载
    • lazy="proxy"            :默认值,proxy具体的取值,取决于另一端的<class>上的lazy值
    • lazy="no-proxy"       :不使用这个选项

测试

映射文件:Parent.hbm.xml

<hibernate-mapping>
<class name="com.qf.entity.Parent" table="parent" lazy="true">
<id name="pid" column="pid">
<generator class="native"/>
</id>
<property name="pname" column="pname"/>
<property name="age" column="age"/>
<set name="childs" cascade="save-update" lazy="true">
<key column="pid"/>
<one-to-many class="com.qf.entity.Children" />
</set>
</class>
</hibernate-mapping>

测试方法

@Test
/**
* HQL简单查询
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction(); Parent parent = session.get(Parent.class, 1L);//第一次发送sql,查询parent对象
Set<Children> set = parent.getChilds();//不发送sql System.out.println(set.size());//第二次发送sql,查询children对象 tx.commit();
}

抓取策略

简介

  • 应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略

    • 也就是通过一个对象抓取到关联的对象,需要发送什么sql,怎么发送,发送成什么格式
  • 抓取策略通常和关联级别的延迟加载一起使用

使用

  • 通常在<set>、<many-to-one>标签上通过fetch属性进行设置
  • 通过fetch+lazy的设置进而优化发送的sql

在set标签上配置

fetch:抓取策略,控制SQL语句格式

  • select :默认值,发送普通的select语句,查询关联对象
  • join :发送一条迫切左外连接查询关联对象
  • subselect :发送一条子查询查询其关联对象

1. fetch="select" lazy="true"(默认)

映射文件中set标签配置

<set name="childs" cascade="save-update" lazy="true" fetch="select">
<key column="pid"/>
<one-to-many class="com.qf.entity.Children" />
</set>

测试方法

@Test
/**
* fetch="select" lazy="true"(默认)
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
Parent parent = session.load(Parent.class, 1L);//不发送sql
Set<Children> childs = parent.getChilds();//发送第一条sql查询parent对象
for (Children children : childs) {//发送第二条sql查询children对象,懒加载,用到的时候才会查询 System.out.println(children.getCname());
}
tx.commit();
}

2. fetch="select" lazy="false"

@Test
/**
* fetch="select" lazy="false"
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
Parent parent = session.load(Parent.class, 1L);//不发送sql
Set<Children> childs = parent.getChilds();//发送两条sql,第一条查询parent对象,第二条sql查询children的集合对象
System.out.println(childs.size());
tx.commit();
}

-------------------------------console-------------------------------

Hibernate:
select
parent0_.pid as pid1_1_0_,
parent0_.pname as pname2_1_0_,
parent0_.age as age3_1_0_
from
parent parent0_
where
parent0_.pid=?
Hibernate:
select
childs0_.pid as pid4_0_0_,
childs0_.cid as cid1_0_0_,
childs0_.cid as cid1_0_1_,
childs0_.cname as cname2_0_1_,
childs0_.sex as sex3_0_1_,
childs0_.pid as pid4_0_1_
from
children childs0_
where
childs0_.pid=?
10

3. fetch="select" lazy="extra"

@Test
/**
* fetch="select" lazy="extra"
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
Parent parent = session.load(Parent.class, 1L);//不发送sql
Set<Children> childs = parent.getChilds();//发送第一条sql查询parent对象
System.out.println(childs.size());//发送第一条sql查询childs总数,比lazy="true"的还要懒惰,需要用到什么结果就只查询什么
tx.commit();
}

-------------------------------console-------------------------------

Hibernate:
select
parent0_.pid as pid1_1_0_,
parent0_.pname as pname2_1_0_,
parent0_.age as age3_1_0_
from
parent parent0_
where
parent0_.pid=?
Hibernate:
select
count(cid)
from
children
where
pid =?
10

4. fetch="join"

发送一条迫切左外连接查询关联对象,此时配置lazy就没有作用了,因为两张表关联的数据都会查出来

@Test
/**
* fetch="join" lazy配置会失效
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
Parent parent = session.load(Parent.class, 1L);//不发送sql
Set<Children> childs = parent.getChilds();//发送一条sql,查询parent和children的所有字段,全部信息都查出来了
System.out.println(childs.size());
tx.commit();
}

-------------------------------console-------------------------------

Hibernate:
select
parent0_.pid as pid1_1_0_,
parent0_.pname as pname2_1_0_,
parent0_.age as age3_1_0_,
childs1_.pid as pid4_0_1_,
childs1_.cid as cid1_0_1_,
childs1_.cid as cid1_0_2_,
childs1_.cname as cname2_0_2_,
childs1_.sex as sex3_0_2_,
childs1_.pid as pid4_0_2_
from
parent parent0_
left outer join
children childs1_
on parent0_.pid=childs1_.pid
where
parent0_.pid=?
10

5. fetch="subselect" lazy="true"

@Test
/**
* fetch="subselect" lazy="true"
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
Parent parent = session.load(Parent.class, 1L);//不发送sql
Set<Children> childs = parent.getChilds();//发送sql查询parent对象
System.out.println(childs.size());//发送sql查询children对象(因为parent只有一个所以子查询效果没有显现)
tx.commit();
}

-------------------------------console-------------------------------

Hibernate:
select
parent0_.pid as pid1_1_0_,
parent0_.pname as pname2_1_0_,
parent0_.age as age3_1_0_
from
parent parent0_
where
parent0_.pid=?
Hibernate:
select
childs0_.pid as pid4_0_0_,
childs0_.cid as cid1_0_0_,
childs0_.cid as cid1_0_1_,
childs0_.cname as cname2_0_1_,
childs0_.sex as sex3_0_1_,
childs0_.pid as pid4_0_1_
from
children childs0_
where
childs0_.pid=?
10

@Test
/**
* fetch="subselect" lazy="true"
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction(); Query query = session.createQuery("from Parent");
List<Parent> list = query.list();
for (Parent parent : list) {
System.out.println(parent.getChilds().size());
} tx.commit();
}

-------------------------------console-------------------------------

Hibernate:
select
parent0_.pid as pid1_1_,
parent0_.pname as pname2_1_,
parent0_.age as age3_1_
from
parent parent0_
Hibernate:
select
childs0_.pid as pid4_0_1_,
childs0_.cid as cid1_0_1_,
childs0_.cid as cid1_0_0_,
childs0_.cname as cname2_0_0_,
childs0_.sex as sex3_0_0_,
childs0_.pid as pid4_0_0_
from
children childs0_
where
childs0_.pid in (
select
parent0_.pid
from
parent parent0_
)
10
10
10

fetch="subselect" lazy="false"和fetch="subselect" lazy="extra"类似

在many-to-one标签上配置

fetch

  • select:默认
  • join

lazy

  • false
  • proxy:默认,proxy具体的取值,取决于另一端的<class>上的lazy的值
    • 另一端<class lazy="true">,那么这一端的lazy="proxy"就相当于lazy="true"
    • 另一端<class lazy="false">,那么这一端的lazy="proxy"就相当于lazy="false"
  • no-proxy

1. fetch="select" lazy="proxy"(默认)

映射文件many-to-one设置

<many-to-one name="p" class="com.qf.entity.Parent" column="pid" lazy="proxy" fetch="select"/>

测试方法

@Test
/**
* lazy="proxy" fetch="select"
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction(); Children children = session.load(Children.class, 1L);
System.out.println("childernName:"+children.getCname());//发送一条sql查询children对象
System.out.println("parentName:"+children.getP().getPname());//发送一条sql查询parent对象 tx.commit(); }

-------------------------------console-------------------------------

Hibernate:
select
children0_.cid as cid1_0_0_,
children0_.cname as cname2_0_0_,
children0_.sex as sex3_0_0_,
children0_.pid as pid4_0_0_
from
children children0_
where
children0_.cid=?
childernName:张三8
Hibernate:
select
parent0_.pid as pid1_1_0_,
parent0_.pname as pname2_1_0_,
parent0_.age as age3_1_0_
from
parent parent0_
where
parent0_.pid=?
parentName:老张

2. fetch="select" lazy="false"

@Test
/**
* lazy="false" fetch="select"
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction(); Children children = session.load(Children.class, 1L);
System.out.println("childernName:"+children.getCname());//发送两条sql,第一条查询children对象,第二条查询parent对象 System.out.println("parentName:"+children.getP().getPname()); tx.commit();
}

-------------------------------console-------------------------------

Hibernate:
select
children0_.cid as cid1_0_0_,
children0_.cname as cname2_0_0_,
children0_.sex as sex3_0_0_,
children0_.pid as pid4_0_0_
from
children children0_
where
children0_.cid=?
Hibernate:
select
parent0_.pid as pid1_1_0_,
parent0_.pname as pname2_1_0_,
parent0_.age as age3_1_0_
from
parent parent0_
where
parent0_.pid=?
childernName:张三8
parentName:老张

3. fetch="join"

发送迫切左外连接,lazy失效

@Test
/**
* fetch="join" lazy失效
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction(); Children children = session.load(Children.class, 1L);
System.out.println("childernName:"+children.getCname());//发送一条sql,同时查询children和parent的所有字段 System.out.println("parentName:"+children.getP().getPname()); tx.commit();
}

-------------------------------console-------------------------------

Hibernate:
select
children0_.cid as cid1_0_0_,
children0_.cname as cname2_0_0_,
children0_.sex as sex3_0_0_,
children0_.pid as pid4_0_0_,
parent1_.pid as pid1_1_1_,
parent1_.pname as pname2_1_1_,
parent1_.age as age3_1_1_
from
children children0_
left outer join
parent parent1_
on children0_.pid=parent1_.pid
where
children0_.cid=?
childernName:张三8
parentName:老张

批量抓取

一次批量获取一批关联对象,设置batch-size属性

  • batch-size="5",表示一次抓取5批(例如:查询所有parent,并使用到每个parent的children,batch-size="5"就表示一次获取5个parent的所有children对象)

一的一方批量抓取多的一方

parent和children,查出所有parent,再找出每个parent所有的children

  • 需要在一的一方的set标签中设置

1. 不配置批量抓取测试

映射文件set标签配置

<set name="childs" cascade="save-update" >
<key column="pid"/>
<one-to-many class="com.qf.entity.Children" />
</set>

测试方法

@Test
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction(); Query query = session.createQuery("from Parent");
List<Parent> list = query.list();
for (Parent parent : list) {
System.out.println("parentName : "+parent.getPname());
Set<Children> childs = parent.getChilds();
for (Children children : childs) {
System.out.println("childrenName : "+children.getCname());
} } tx.commit();
}

-------------------------------console-------------------------------

Hibernate:
select
parent0_.pid as pid1_1_,
parent0_.pname as pname2_1_,
parent0_.age as age3_1_
from
parent parent0_
parentName : 老张
Hibernate:
select
childs0_.pid as pid4_0_0_,
childs0_.cid as cid1_0_0_,
childs0_.cid as cid1_0_1_,
childs0_.cname as cname2_0_1_,
childs0_.sex as sex3_0_1_,
childs0_.pid as pid4_0_1_
from
children childs0_
where
childs0_.pid=?
childrenName : 张三2
childrenName : 张三0
childrenName : 张三8
parentName : 老李
Hibernate:
select
childs0_.pid as pid4_0_0_,
childs0_.cid as cid1_0_0_,
childs0_.cid as cid1_0_1_,
childs0_.cname as cname2_0_1_,
childs0_.sex as sex3_0_1_,
childs0_.pid as pid4_0_1_
from
children childs0_
where
childs0_.pid=?
childrenName : 李四9
childrenName : 李四6
childrenName : 李四3
parentName : 老王
Hibernate:
select
childs0_.pid as pid4_0_0_,
childs0_.cid as cid1_0_0_,
childs0_.cid as cid1_0_1_,
childs0_.cname as cname2_0_1_,
childs0_.sex as sex3_0_1_,
childs0_.pid as pid4_0_1_
from
children childs0_
where
childs0_.pid=?
childrenName : 王五2
childrenName : 王五7
childrenName : 王五4

2. 配置批量抓取测试

映射文件set标签配置

<set name="childs" cascade="save-update" batch-size="3">
<key column="pid"/>
<one-to-many class="com.qf.entity.Children" />
</set>

测试方法

@Test
/**
* batch-size="3"
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction(); Query query = session.createQuery("from Parent");
List<Parent> list = query.list();
for (Parent parent : list) {
System.out.println("parentName : "+parent.getPname());
Set<Children> childs = parent.getChilds();
for (Children children : childs) {
System.out.println("childrenName : "+children.getCname());
} } tx.commit();
}

-------------------------------console-------------------------------

Hibernate:
select
parent0_.pid as pid1_1_,
parent0_.pname as pname2_1_,
parent0_.age as age3_1_
from
parent parent0_
parentName : 老张
Hibernate:
select
childs0_.pid as pid4_0_1_,
childs0_.cid as cid1_0_1_,
childs0_.cid as cid1_0_0_,
childs0_.cname as cname2_0_0_,
childs0_.sex as sex3_0_0_,
childs0_.pid as pid4_0_0_
from
children childs0_
where
childs0_.pid in (
?, ?, ?
)
childrenName : 张三8
childrenName : 张三0
childrenName : 张三2
parentName : 老李
childrenName : 李四3
childrenName : 李四6
childrenName : 李四9
parentName : 老王
childrenName : 王五2
childrenName : 王五7
childrenName : 王五4

多的一方批量抓取一的一方

查询所有children,找出每个children的parent

  • 需要在一的一方的class标签中设置batch-size

映射文件class标签配置

<class name="com.qf.entity.Parent" table="parent" batch-size="3">

测试方法

@Test
/**
* batch-size="3"
*/
public void query() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction(); Query query = session.createQuery("from Children");
List<Children> list = query.list();
for (Children children : list) {
System.out.println("childrenName : "+children.getCname());
System.out.println("parentName : "+children.getP().getPname());
} tx.commit();
}

-------------------------------console-------------------------------

Hibernate:
select
children0_.cid as cid1_0_,
children0_.cname as cname2_0_,
children0_.sex as sex3_0_,
children0_.pid as pid4_0_
from
children children0_
childrenName : 张三8
Hibernate:
select
parent0_.pid as pid1_1_0_,
parent0_.pname as pname2_1_0_,
parent0_.age as age3_1_0_
from
parent parent0_
where
parent0_.pid in (
?, ?, ?
)
parentName : 老张
childrenName : 张三0
parentName : 老张
childrenName : 张三2
parentName : 老张
childrenName : 李四9
parentName : 老李
childrenName : 李四6
parentName : 老李
childrenName : 李四3
parentName : 老李
childrenName : 王五7
parentName : 老王
childrenName : 王五2
parentName : 老王
childrenName : 王五4
parentName : 老王

  

十、hibernate的延迟加载和抓取策略的更多相关文章

  1. hibernate的延迟加载和抓取策略

    一,延迟加载 1.实体类延迟加载 通过代理机制完成,由javassist类库实现运行时代理,修改实体类的字节码实现了运行时代理     <class lazy="true|false& ...

  2. hibernate 延迟加载和抓取策略

    一.延迟加载 1.简单查询get,load 针对对象本身延迟或即时 当使用load方法来得到一个对象时,此时hibernate会使用延迟加载的机制来加载这个对象,即:当我们使用session.load ...

  3. 【转】hibernate延迟加载和抓取策略

    一.延迟加载 1.简单查询get,load 针对对象本身延迟或即时 当使用load方法来得到一个对象时,此时hibernate会使用延迟加载的机制来加载这个对象,即:当我们使用session.load ...

  4. Hibernate fetching strategies(抓取策略)

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

  5. hibernate_06_hibernate的延迟加载和抓取策略

    1.延迟加载 1>类级别的延迟加载 指的是通过oad方法查询某个对象的时候,是否采用延迟, session. load(Customer class1L) 类级别延迟加载通过<class& ...

  6. Hibernate 性能优化之抓取策略

    fetch 抓取策略 前提条件:必须是一个对象操作其关联对象. 1. 根据一的一方加载多的一方,在一的一方集合中,有三个值:join/select/subselect 2.根据多的一方加载一的一方, ...

  7. Hibernate框架笔记04HQL_QBC查询详解_抓取策略优化机制

    目录 1. Hibernate的查询方式 1.1 方式一:OID查询 1.2 方式二:对象导航查询 1.3 方式三:HQL方式 1.4 方式四:QBC查询 1.5 方式五:SQL查询 2. 环境搭建 ...

  8. Hibernate之加载策略(延迟加载与即时加载)和抓取策略(fetch)

    假设现在有Book和Category两张表,表的关系为双向的一对多,表结构如下: 假设现在我想查询id为2的那本书的书名,使用session.get(...)方法: Session session=H ...

  9. Hibernate(十四)抓取策略

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

随机推荐

  1. python常用函数 L

    lstrip(str) 删除字符串左边的字符,支持传入参数. 例子: ljust(int) 格式化字符串,左对齐,支持传入填充值. 例子: loads(json) 将json字符串转换为dict. 例 ...

  2. bzoj4036 [HAOI2015]按位或 状压DP + MinMax 容斥

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4036 题解 变成 \(2^n-1\) 的意思显然就是每一个数位都出现了. 那么通过 MinMa ...

  3. js 中HTML的 onkeycode 和onkeydown属性事件

    <!DOCTYPE html><html><head><script>function displayResult(){var x=document.g ...

  4. abstractmethod

    a class with an abstract method cannot be instantiated (that is, we cannot create an instance by cal ...

  5. JS中算法之排序算法

    1.基本排序算法 1.1.冒泡排序 它是最慢的排序算法之一. 1.不断比较相邻的两个元素,如果前一个比后一个大,则交换位置. 2.当比较完第一轮的时候最后一个元素应该是最大的一个. 3.按照步骤一的方 ...

  6. S1.2 Python开发规范指南

    参考链接 Python风格规范 分号 不要在行尾加分号, 也不要用分号将两条命令放在同一行. 行长度 每行不超过80个字符 例外: 长的导入模块语句 注释里的URL 不要使用反斜杠连接行. Pytho ...

  7. UIStakView的添加与移除

    subView和arrangedSubView对于Stack View的子控件添加和移除,我们是这样描述的. 添加-->(Stack View管理的subview) addArrangedSub ...

  8. 2016年Esri技术公开课全年资料分享

    大家好,2016年的公开课活动在上周全部结束,感谢大家的支持. 2016年的公开课共进行20期,共有24位讲师参与,公开课视频播放.课件下载次数累计超10万次,在这里衷心的感谢大家的积极参与和分享精神 ...

  9. 家用NAS配置方案

    对家用用户而言,NAS即一台下载机,硬件需要满足以下几点: 1.稳定性:24×7稳定无故障运行. 2.拓展性:较多的硬盘槽位,便于容量扩容: 3.体积小巧:占地面积小,便于放置. 4.方便远程管理:无 ...

  10. java 异常处理try+catch

    在整个异常处理机制中,异常在系统中进行传递,传递到程序员认为合适的位置,就捕获到该异常,然后进行逻辑处理,使得项目不会因为出现异常而崩溃.为了捕获异常并对异常进行处理,使用的捕获异常以及处理的语法格式 ...