延迟加载:控制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. Docker Swanm集群配置

    首先 可以用ContOS虚拟机   克隆  5个虚拟机,注意(克隆主机必须装了Docker,克隆后,克隆机都会有Docker) 配置 网络 克隆CentOS虚拟机 最后和到如下结果 打开2377端口 ...

  2. 一、表单和ajax中的post请求&&后台获取数据方法

    一.表单和ajax中的post请求&&后台获取数据方法 最近要做后台数据接收,因为前台传来的数据太过于混乱,所以总结了一下前台数据post请求方法,顺便写了下相对应的后台接收方法. 前 ...

  3. 解决图片插入word文档后清晰度降低的问题

    解决图片插入word文档后清晰度降低的问题 在默认情况下,word程序会自动压缩插入word文档中的图片以减小整个word文档的.当我们需要插入word文档中的图片保持原始清晰度时,可以通过设置wor ...

  4. 条款7:为多态基类析构函数声明为virtual

    基类指针指向子类对象. 子类对象必须位于堆.因此为了避免泄漏内存资源,当指针不使用时,delete掉每一个对象非常重要.但是如果基类的析构函数不声明为virtual.那么指向子类对象的指针delete ...

  5. Minor GC与Full GC分别在什么时候发生?

    Minor GC 当Eden区没有足够空间进行分配时,虚拟机就会进行一次Minor GC 新生代的垃圾收集动作,采用的是复制算法 对于较大的对象,在Minor GC的时候可以直接进入老年代 Full ...

  6. Codeforces Round #454 (Div. 1) CodeForces 906D Power Tower (欧拉降幂)

    题目链接:http://codeforces.com/contest/906/problem/D 题目大意:给定n个整数w[1],w[2],……,w[n],和一个数m,然后有q个询问,每个询问给出一个 ...

  7. BZOJ 2141 排队 (CDQ分治)

    [BZOJ2141]排队 这道题和动态逆序对比较像(BZOJ-3295 没做过的同学建议先做这题),只是删除操作变成了交换.解法:交换操作可以变成删除加插入操作,那么这题就变成了 (时间,位置,值)的 ...

  8. Nginx环境部署

    下载Nginx wget nginx.tar.gz http://nginx.org/download/nginx-1.17.1.tar.gz 解压源码 tar -zxvf nginx-1.17.1. ...

  9. Ubunton

    Ubunton 装完机后 root账户进不去 没密码 在自己账号下 sudo passwd 输入两次密码就是root的密码 之后就可以su root 密码进入了 外部客户端sftp方式连接: sudo ...

  10. Ubuntu14.04安装Ruby2.2方法

    直接使用系统的sudo apt-get install ruby2.0安装后,ruby -v显示ruby的版本依然是ruby 1.9. 以下方法可以顺序地在Ubuntu14.04安装Ruby2.2 s ...