Hibernate之抓取策略
Posted WWWYC
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate之抓取策略相关的知识,希望对你有一定的参考价值。
时间:2017-1-23 19:08
——区分延迟和立即检索
1、立即检索
当执行某行代码时,会马上发出SQL语句进行查询。
例如:get()
2、延迟检索
当执行某行代码时,不会马上发出SQL语句,只有当真正使用对象时,才会向数据库发出SQL语句。
例如:load()
3、示例代码
/*
* 区分立即检索和延迟检索
*/
public void fun1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 立即检索
Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer);
/*
* 延迟检索
* 当持久化类设置为final之后,延迟检索就失效了,因为不能生成代理对象
* 在Customer.hbm.xml的<class>标签上配置lazy="false",表示不支持延迟检索
*/
Customer customer2 = (Customer) session.load(Customer.class, 1);
// 初始化代理对象
System.out.println(customer2);
Hibernate.initialize(customer2); // 会立即检索
tx.commit();
session.close();
}
——类级别检索和关联级别检索
1、类级别的检索:
1)类级别可选的检索策略包括立即检索和延迟检索,默认为延迟检索。
2)类级别的检索策略可以通过<class>元素的lazy属性进行设置。
3)如果程序加载一个对象的目的是为了访问它的属性,可以采取立即检索的方式,如果程序加载一个持久化对象的目的仅仅是为了获得它的引用,可以采用延迟检索。
4)无论<class>元素的lazy属性是true还是false,Session的get()方法以及Query的list()方法在类级别总是使用立即检索的策略。
5)若<class>元素的lazy属性为true或取默认值,Session的load()方法不会执行查询数据表的select语句,而是仅返回代理对象的实例,该代理对象实例有如下特征:
* 由Hibernate在运行时采用javassist工具动态生成
* Hibernate创建代理对象实例时,仅初始化其OID属性
* 在应用程序第一次访问代理实例的非OID属性时,Hibernate会初始化代理类实例。
2、关联级别的检索
1)在映射文件中,用<set>元素来配置一对多关联及多对多关联关系,<set>元素中有lazy和fetch属性:
2)lazy:主要决定orders集合被初始化的时机,即是否在Customer对象初始化时被加载,还是在程序访问orders集合时被初始化。
3)fetch:取值为select或subselect时,决定初始化orders的查询语句的形式,若取值为join,则决定orders集合被初始化的时机。
4)若把fetch设置为“join”,lazy属性将被忽略。
——一方关联多方的情况
在<set>元素中包含fetch和lazy属性:
* fetch:控制SQL语句的类型
> join:采用迫切左外连接查询
> select:默认值
> subselect:发送子查询来查询关联对象
* lazy:控制关联对象的检索是否采用延迟
> true:默认值,查询关联对象采用延迟检索
> false:查询关联对象不使用延迟检索
> extra:特懒
join:一次查完
select:分多次查完
lazy="true":延迟查询
lazy="false":不延迟查询
如果fetch="join",那么lazy属性将被忽略。
示例代码:
1)<set>集合中没有配置fetch和lazy的情况
默认值:fetch="select" lazy="true"
// 只发送查询客户的SQL语句,没有发送查询订单的SQL
Customer customer = (Customer) session.get(Customer.class, 1);
// 使用客户订单信息的时候,才会发送查询订单的SQL语句
System.out.println(customer.getOrders().size());
SQL:
Hibernate:
select
customer0_.cid as cid0_0_,
customer0_.cname as cname0_0_
from
customer customer0_
where
customer0_.cid=?
Hibernate:
select
orders0_.cid as cid0_1_,
orders0_.oid as oid1_,
orders0_.oid as oid1_0_,
orders0_.addr as addr1_0_,
orders0_.cid as cid1_0_
from
order_table orders0_
where
orders0_.cid=?
2)在<set>标签中配置fetch="join",lazy会被忽略
* 只要fetch设置为join,会采用迫切左外连接进行查询
// 直接发送一条迫切左外连接查询,查询全部数据,包括订单信息
Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer.getOrders().size());
SQL:
Hibernate:
select
customer0_.cid as cid0_1_,
customer0_.cname as cname0_1_,
orders1_.cid as cid0_3_,
orders1_.oid as oid3_,
orders1_.oid as oid1_0_,
orders1_.addr as addr1_0_,
orders1_.cid as cid1_0_
from
customer customer0_
left outer join
order_table orders1_
on customer0_.cid=orders1_.cid
where
customer0_.cid=?
3)在<set>标签中配置fetch="select" lazy="extra"
* lazy="extra" 极其懒惰
// 只查询Customer的信息,不查询关联对象信息
Customer customer = (Customer) session.get(Customer.class, 1);
// 当查询数量时,只会发送:select count(*) from order_table where oid = ?
System.out.println(customer.getOrders().size());
// 当查询订单时,才会查询全部订单信息:selecg * from order_table
System.out.println(customer.getOrders());
SQL:
Hibernate:
select
customer0_.cid as cid0_0_,
customer0_.cname as cname0_0_
from
customer customer0_
where
customer0_.cid=?
Hibernate:
select
count(oid)
from
order_table
where
cid =?
Hibernate:
select
orders0_.cid as cid0_1_,
orders0_.oid as oid1_,
orders0_.oid as oid1_0_,
orders0_.addr as addr1_0_,
orders0_.cid as cid1_0_
from
order_table orders0_
where
orders0_.cid=?
4)在<set>标签中配置fetch="subselect" lazy="true"
* 使用subselect的时候需要使用Query接口进行测试,因为需要查询多个客户,才能看到子查询的效果
* 查询一个客户和查询多个客户有什么区别?
* > 如果只有一个客户会使用:=
* > 如果有多个客户会使用:in
List<Customer> list = session.createQuery("from Customer").list();
for(Customer c : list){
System.out.println(c.getOrders().size());
}
SQL:
Hibernate:
select
customer0_.cid as cid0_,
customer0_.cname as cname0_
from
customer customer0_
Hibernate:
select
orders0_.cid as cid0_1_,
orders0_.oid as oid1_,
orders0_.oid as oid1_0_,
orders0_.addr as addr1_0_,
orders0_.cid as cid1_0_
from
order_table orders0_
where
orders0_.cid in (
select
customer0_.cid
from
customer customer0_
)
——多方关联一方的情况
1、多对一和一对一关联的检索策略,和<set>一样,<many-to-one>元素也有一个lazy属性和fetch属性:
* 若fetch属性设为join,那么lazy属性被忽略。
* 迫切左外连接检索策略的优点在于比立即检索策略使用的select语句更少。
* 无代理延迟检索需要增强持久化类的字节码才能实现。
2、Query的list()方法会忽略映射文件配置的迫切左外连接检索策略,而采用延迟检索或立即检索策略,根据Customer类级别的lazy属性进行检索,lazy="true"为延迟检索,lazy="false"为立即检索。
3、如果在关联级别使用了延迟加载或立即加载检索策略,可以设定批量检索的大小,以帮助提高延迟检索或立即检索的运行性能。
4、<many-to-one>
* fetch:控制SQL语句发送格式
> join:使用迫切左外连接查询,lazy会被忽略
> select:发送多条SQL检索对象
* lazy:关联对象检索的时候,是否采用延迟
> false:不延迟
> proxy:使用代理,检索订单时是否马上检索客户,由Customer对象的映射文件中<class>元素的lazy属性来决定
> no-proxy:不使用代理
示例代码:
1)没有在<many-to-one>标签上进行配置
* 发送多条SQL来查询订单中的信息
// 只查询订单的记录,拿到的是关联Customer的引用
Order order = (Order) session.get(Order.class, 1);
// 使用订单的Customer对象时,会发送一条SQL查询订单关联的客户对象的信息
System.out.println(order.getCustomer().getCname());
SQL:
Hibernate:
select
order0_.oid as oid1_0_,
order0_.addr as addr1_0_,
order0_.cid as cid1_0_
from
order_table order0_
where
order0_.oid=?
Hibernate:
select
customer0_.cid as cid0_0_,
customer0_.cname as cname0_0_
from
customer customer0_
where
customer0_.cid=?
2)在<many-to-one>标签上进行配置
* fetch="join" lazy会被忽略
* 发送迫切左外连接
// 发送一条迫切左外连接,将全部信息都获取到,并且封装到对象中
Order order = (Order) session.get(Order.class, 1);
System.out.println(order.getCustomer().getCname());
SQL:
Hibernate:
select
order0_.oid as oid1_1_,
order0_.addr as addr1_1_,
order0_.cid as cid1_1_,
customer1_.cid as cid0_0_,
customer1_.cname as cname0_0_
from
order_table order0_
left outer join
customer customer1_
· on order0_.cid=customer1_.cid
where
order0_.oid=?
3)在<many-to-one>标签上设置
* fetch="select" lazy="false"
// 发送多条SQL,不延迟,一次查完
Order order = (Order) session.get(Order.class, 1);
System.out.println(order.getCustomer().getCname());
SQL:
Hibernate:
select
order0_.oid as oid1_0_,
order0_.addr as addr1_0_,
order0_.cid as cid1_0_
from
order_table order0_
where
order0_.oid=?
Hibernate:
select
customer0_.cid as cid0_0_,
customer0_.cname as cname0_0_
from
customer customer0_
where
customer0_.cid=?
——批量抓取
1、从一方批量抓取多方记录
在Customer.hbm.xml的<set>标签上配置batch-size="2"
* 表示一次查询两个
示例代码:
List<Customer> list = session.createQuery("from Customer").list();
for(Customer customer : list){
for(Order order : customer.getOrders()){
System.out.println(order.getAddr());
}
}
SQL:
Hibernate:
select
customer0_.cid as cid0_,
customer0_.cname as cname0_
from
customer customer0_
Hibernate:
select
orders0_.cid as cid0_1_,
orders0_.oid as oid1_,
orders0_.oid as oid1_0_,
orders0_.addr as addr1_0_,
orders0_.cid as cid1_0_
from
order_table orders0_
where
orders0_.cid in (
?, ?
)
奎文3
奎文4
奎文1
奎文7
奎文2
奎文0
奎文8
奎文6
奎文9
奎文5
高新2
高新9
高新3
高新8
高新1
高新4
高新0
高新5
高新6
高新7
Hibernate:
select
orders0_.cid as cid0_1_,
orders0_.oid as oid1_,
orders0_.oid as oid1_0_,
orders0_.addr as addr1_0_,
orders0_.cid as cid1_0_
from
order_table orders0_
where
orders0_.cid=?
潍城4
潍城5
潍城6
潍城1
潍城7
潍城0
潍城9
潍城2
潍城3
潍城8
2、从多方批量抓取一方记录
不能在多方设置batch-size属性,需要在一方的<class>标签上设置batch-size。
——总结
1、立即检索
2、延迟检索
配置fetch和lazy属性
* 配置立即检索
> lazy="false"
> 持久化类设置为final
> 在调用方法的时候,初始化代理对象
3、延迟
类级别的延迟:
<class>元素上设置lazy
关联级别的延迟:
<set> / <many-to-many> / <one-to-one>
4、fetch属性
* <set>集合上的fetch:
> join:强制使用迫切左外连接
> select:默认值,会发送多条SQL语句
> subselect:使用子查询
* 在<many-to-one>标签上使用
> select:默认值,发送多条SQL
> join:强制使用迫切左外连接
5、lazy属性
* <set>
> true:默认值,延迟
> false:不采用延迟
> extra:极其懒惰
* <many-to-one>
> proxy:根据另一方的<calss>元素配置的lazy来确定是否使用延迟。
> false:不采用延迟。
> no-proxy
6、batch-size批量抓取
默认情况下只会检索一条记录,如果想要批量抓取多条记录,可以使用batch-size进行检索。
以上是关于Hibernate之抓取策略的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate fetch 抓取策略
Hibernate--抓取策略
hibernate检索策略(抓取策略)
Hibernate学习 —— 抓取策略(注解方式)
Hibernate - HQL_QBC查询详解--抓取策略优化机制
hibernate_06_hibernate的延迟加载和抓取策略