Hibernate 提升性能的一种手段是抓取策略,可以在获取关联对象的时候,对发送的语句进行优化,但是往往抓取策略需要和延迟加载一起使用提升性能。
下面的类级别查询优化和关联级别查询优化需要用到延迟加载或者延迟加载和抓取策略一起协同来优化查询,提升性能。
我们测试下面的程序一起玩玩延迟加载是什么,又是如何优化查询的。。
@Test
// get方法 : 立即加载.执行方法时立即发送sql语句查询结果
public void fun1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
Customer c = session.get(Customer.class, 2l);
System.out.println(c);
//----------------------------------------------------
tx.commit();
session.close();
}
通过打断点测试发现,执行完 get().后就立即打印了 sql 语句。
执行下面程序
@Test
// load方法(默认):是在执行时,不发送任何sql语句.返回一个对象.使用该对象时,才执行查询.
// 延迟加载: 仅仅获得没有使用.不会查询.在使用时才进行查询.
// 是否对类进行延迟加载: 可以通过在class元素上配置lazy属性来控制.
//lazy:true (默认值)加载时,不查询.使用时才查询
//lazy:false 加载时立即查询.
public void fun2(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
Customer c = session.load(Customer.class, 1l);
System.out.println(c);
//----------------------------------------------------
tx.commit();
session.close();
}
测试发现,执行了 load() 并没有发送sql语句,而是在需要用到该对象 c 时才发送 sql 语句,这就是延迟加载,上面注释也解释了
我们在 Customer 映射文件的 标签上添加lazy属性,并把属性值设为 flase
再执行上面程序,发现和 get() 方法一样,在执行时就发送了 sql 语句。
PS:我们一般都不进行修改,采用 lazy=“true” 即可
关联级别查询,就是对象图导航检索,根据一个实体对象获取与其关联的实体对象。而关联级别的延迟加载就是在查询其关联实体对象的时候是否采用延迟加载。同时,为了进一步提升程序的性能,还涉及到抓取策略。
我把关联级别查询优化分为两种情况,关联集合查询优化策略和关联属性查询优化
也就是在 set 标签上添加 lazy 和 fetch 属性设置优化策略
lazy属性: 决定是否延迟加载
true(默认值): 延迟加载,懒加载
false: 立即加载
extra: 极其懒惰
fetch属性: 决定加载策略.使用什么类型的sql语句加载集合数据
select(默认值): 单表查询加载
join: 使用多表查询加载集合
subselect:使用子查询加载集合
这样来看在 set上配置 fetch 有三个值,lazy 有三个值,这样就会产生很多种的效果。其实不慌,因为 fetch 如果设置为 join,azy 属性就会失效了。
下面我们通过改变 set 标签上这两个属性的值来查看每种取值的效果。
// 集合级别的关联
// fetch:select 单表查询
// lazy:true 使用时才加载集合数据.
@Test
public void fun1() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// ----------------------------------------------------
Customer c = session.get(Customer.class, 2l);
Set linkMens = c.getLinkMens();// 关联级别
System.out.println(linkMens);
// ----------------------------------------------------
tx.commit();
session.close();
}
测试发现,Hibernate 都是通过单表查询来查询 Customer 和 linkMens ,并且启动了延迟加载,在使用 linkMens 对象时才加载集合数据
// 集合级别的关联
// fetch:select 单表查询
// lazy:false 立即加载集合数据
@Test
public void fun2() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// ----------------------------------------------------
Customer c = session.get(Customer.class, 2l);
Set linkMens = c.getLinkMens();// 关联级别
System.out.println(linkMens);
// ----------------------------------------------------
tx.commit();
session.close();
}
同样也是通过单表查询,但在加载 Customer 时,也就把与 Customer 关联的集合 linkMens 加载了
// 集合级别的关联
// fetch:select 单表查询
// lazy:extra 极其懒惰.与懒加载效果基本一致. 如果只获得集合的size.只查询集合的size(count语句)
@Test
public void fun3() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// ----------------------------------------------------
Customer c = session.get(Customer.class, 2l);
Set linkMens = c.getLinkMens();// 关联级别
System.out.println(linkMens.size());
System.out.println(linkMens);
// ----------------------------------------------------
tx.commit();
session.close();
}
与懒加载效果基本一致. 如果只获得集合的 size.只查询集合的 size ( count 语句),并没有加载 linkMens 的数据,在使用 linkMens 时才加载
// 集合级别的关联
// fetch:join 多表查询
// lazy:true|false|extra 失效.立即加载.
@Test
public void fun4() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// ----------------------------------------------------
Customer c = session.get(Customer.class, 2l);
Set linkMens = c.getLinkMens();// 关联级别
System.out.println(linkMens.size());
System.out.println(linkMens);
// ----------------------------------------------------
tx.commit();
session.close();
}
因为抓取策略时是 join 多表查询,所以通过外连接获取 Customer 时也把 linkMens 也加载了,因此无论 lazy 属性取值如何,都失效。
@Test
// fetch: subselect 发送一条子查询语句查询其关联对象
// lazy: true 懒加载
public void fun5() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// ----------------------------------------------------
String hql = "from Customer";
Query query = session.createQuery(hql);
List list = query.list();
for (Customer c : list) {
System.out.println(c);
System.out.println(c.getLinkMens().size());
System.out.println(c.getLinkMens());
}
// ----------------------------------------------------
tx.commit();
session.close();
}
通过懒加载就不用说了,接着,第一次循坏加载 linkMens 时发送的一条子查询语句,sql 语句如下:
由于第一循环已经把全部 Customer 的所有 linkMens 加载完毕(留意 where 后的语句),所以在第二次…循环时就不用加载了,直接遍历数据了
@Test
// fetch: subselect 子查询
// lazy: false 立即加载
public void fun6() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// ----------------------------------------------------
String hql = "from Customer";
Query query = session.createQuery(hql);
List list = query.list();
for (Customer c : list) {
System.out.println(c);
System.out.println(c.getLinkMens().size());
System.out.println(c.getLinkMens());
}
// ----------------------------------------------------
tx.commit();
session.close();
}
根据前面的测试效果,这种情况相信你也能猜的出来效果会如何。是的,因为时立即加载,所以在加载 Customer 时,就把与 Customer 关联的集合加载出来了,并且应用了子查询
@Test
// fetch: subselect 子查询
// lazy: extra 极其懒惰
public void fun7() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// ----------------------------------------------------
String hql = "from Customer";
Query query = session.createQuery(hql);
List list = query.list();
for (Customer c : list) {
System.out.println(c);
System.out.println(c.getLinkMens().size());
System.out.println(c.getLinkMens());
}
// ----------------------------------------------------
tx.commit();
session.close();
}
同样,根据前面的经验, 如果只获得集合的 size.只查询集合的 size ( count 语句),并没有加载 linkMens 的数据,在使用 linkMens 时才加载。
然而,测试了这集中情况,你们觉得那种属性的混合提升性能最明显呢,当然时 fetct=“select”,lazy=“true”,也就是默认值。在需要的时候才取加载,效率自然时最好的了。
最后还是要根据需求而设置,一般情况下时使用默认值即可!
也就是在 many-to-one 标签上添加 lazy 和 fetch 属性设置优化策略
lazy属性: 决定是否延迟加载
proxy(默认值): 是否采用延迟加载,取决于一的一方类上的lazy属性
false: 检索关联对象时,不使用延迟加载
no-proxy: 不用研究
fetch属性: 决定加载策略.使用什么类型的sql语句加载关联对象
select(默认值): 单表查询加载
join: 使用多表查询加载集合
下面我们通过程序来查看每种取值的效果。
@Test
//fetch:select 单表查询
//lazy:proxy
//customer-true 懒加载
public void fun1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
LinkMan lm = session.get(LinkMan.class, 3l);
Customer customer = lm.getCustomer();
System.out.println(customer);
//----------------------------------------------------
tx.commit();
session.close();
}
@Test
//fetch:join 多表
//lazy: 失效
public void fun3(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
LinkMan lm = session.get(LinkMan.class, 3l);
Customer customer = lm.getCustomer();
System.out.println(customer);
//----------------------------------------------------
tx.commit();
session.close();
}
@Test
//fetch:select 单表查询
//lazy:proxy
//customer-false 立即加载
public void fun2(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
LinkMan lm = session.get(LinkMan.class, 3l);
Customer customer = lm.getCustomer();
System.out.println(customer);
//----------------------------------------------------
tx.commit();
session.close();
}
通过关联集合查询的优化策略学习,再学关联对象查询的优化策略就很好理解,单看属性值的变化就可以猜到效果会如何。没错,就是如你们所想,相信自己!故不再做详细说明。
结论:为了提高性能,fetch 和 lazy 属性也都是使用默认值!
在抓取的策略中有一种叫做批量抓取,就是同时查询多个对象的关联对象的时候,可以采用批量抓取进行优化。当然这个就不是特别重要了。
如果要实现批量的抓取效果,可以通过 batch-size 来配置
例如查询客户批量查询联系人时,可以在客户的映射文件中的 set 标签上设置 batch-size=“3”
相反,查询联系人批量查询客户时,可以在客户的映射文件中的 class 标签上设置 batch-size="?"