延迟加载:控制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 : 老王