Hibernate的延迟加载和抓取策略

1. Hibernate延迟加载

延迟加载(也称为懒加载)是Hibernate关联关系对象默认的加载方式,延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。

通常将延迟加载分为两类:一类叫做类级别延迟,另一类叫做关联级别的延迟。类级别的延迟指的是查询某个对象的时候,是否采用有延迟,这个通常在<class>标签上配置lazy属性。关联级别的延迟指的是,查询一个对象的关联对象的时候是否采用延迟加载,这个通常在<set>或<many-to-one>上配置lazy属性。

1.1 类级别的延迟加载

使用load方法检索某个对象的时候,这个类是否采用延迟加载的策略,就是类级别的延迟。类级别的延迟一般在<class>上配置lazy属性,lazy的默认值是true,默认是延迟加载的,所以使用load方法去查询的时候,不会马上发送SQL语句,当真正使用该对象的时候,才会发送SQL语句。

Customer customer = session.load(Customer.class, 1L);

其实如果不想使用延迟加载也有很多种方法,当然最简单的就是将这个类的映射文件上的lazy设置为false,当然也可以将这个持久化类改为final修饰,如果改为final修饰的话,就无法生成代理类,就会使延迟加载失效。这是类级别的延迟加载,类级别的延迟加载一般我们不进行修改, 采用默认值lazy="true"就可以了。

其实主要的是关联级别的延迟加载,关联级别的延迟加载指的是查询到某个对象以后,检索它的关联对象的时候是否采用延迟加载。

1.2 关联级别的延迟加载

Customer customer = session.get(Customer.class, 1L);
Set<LinkMan> linkMans = customer.getLinkMans();

通过客户查询其关联的联系人对象,在查询联系人的时候是否采用延迟加载称为是关联级别的延迟。关联级别的延迟通常是在<set>或<many-to-one>上来进行配置。

  • <set>标签上的lazy通常有三个取值:
    true:默认值,采用延迟加载。
    false:检索关联对象的时候,不采用延迟加载。
    extra:及其懒惰的。
  • <many-to-one>标签上的lazy通常有三个取值:
    proxy:默认值,是否采用延迟取决于一的一方类上<class>的lazy属性的值
    false:检索关联对象的时候,不采用延迟加载。

延迟加载介绍过了,延迟加载也是往往和抓取策略一起使用提升开发的程序的性能的,那么接下来我们要来研究抓取策略了。

2. 抓取策略

抓取策略指的是查询到某个对象的时候,如何抓取其关联对象。这个也可以通过配置完成,在关联对象的标签上配置fetch属性,关联上就分为是在<set>或<many-to-one>上,也都有不同的取值。

  • <set>标签上的fetch通常有两个取值:
    select:默认值,发送的是普通的select语句查询。
    join:发送一条迫切左外连接去查询。
    subselect:发送一条子查询语句查询其关联对象。
  • <many-to-one>标签上的fetch有两个取值:
    select:默认值,发送一条普通的select语句查询关联对象。
    join:发送一条迫切左外连接语句查询其关联对象。

接下来我们通过代码测试的方式来查看每种取值的效果。

2.1 在<set>标签上的fetch和lazy

//默认情况
//fetch:select 单表查询
//lazy:true 使用时才加载集合数据
public void test1(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();
	
	Customer c = session.get(Customer.class, 1L); //发送一条select语句查询1号客户
	Set<LinkMan> linkMens = c.getLinkMans(); //发送一条select语句查询1号客户的所有联系人
	System.out.println(linkMans);
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

//fetch:select 单表查询
//lazy:false 立即加载集合数据
public void test2(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();

	Customer c = session.get(Customer.class, 1L); //发送两条select语句,一条查询1号客户,一条查询1号客户的所有联系人
	Set<LinkMan> linkMans = c.getLinkMans(); //不发送sql
	System.out.println(linkMans);
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

//fetch:select 单表查询
//lazy:extra 极其懒惰,与懒加载效果基本一致. 如果只获得集合的size.只查询集合的size(count语句)
public void test3(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();

	Customer c = session.get(Customer.class, 1L); //发送一条select语句查询1号客户
	System.out.println(c.getLinkMans().size()); //发送一条select count(*)语句统计个数
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

//fetch:join 多表查询
//lazy:true|false|extra 失效,立即加载
public void test4(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();

	Customer c = session.get(Customer.class, 1L); //发送一条迫切左外连接语句直接将关联的对象一起查询了
	System.out.println(c.getLinkMans().size()); //不发送sql
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

//fetch: subselect 子查询
//lazy: true 使用时才加载集合数据
public void test5(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();

	Query query = session.createQuery("from Customer");
	List<Customer> list = query.list(); //发送查询所有客户的语句
	for(Customer c : list){
		System.out.println(c.getLinkMans().size()); //发送一条子查询,查询客户关联的联系人
	}
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

//fetch: subselect 子查询
//lazy: false 立即加载集合数据
public void test6(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();

	Query query = session.createQuery("from Customer");
	List<Customer> list = query.list(); //发送两条select语句,一条查询所有客户,一条子查询查询客户的联系人
	for(Customer c : list){
		System.out.println(c.getLinkMans().size()); //不发送sql
	}
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

//fetch: subselect 子查询
//lazy: extra 极其懒惰
public void test7(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();

	Query query = session.createQuery("from Customer");
	List<Customer> list = query.list(); //发送查询所有客户的语句
	for(Customer c : list){
		System.out.println(c.getLinkMans().size()); //发送一条select count(*)语句统计个数,而不是一条子查询
	}
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

2.1 在<many-to-one>标签上的fetch和lazy

//默认情况
//fetch:select	单表查询
//lazy:proxy (此时一对多中一方类上的lazy属性值为默认值true)
public void test1(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();
	
	LinkMan linkMan = session.get(LinkMan.class, 1L); //发送一条select语句查询1号联系人
	Customer customer = linkMan.getCustomer(); //发送一条select语句查询1号联系人对应的客户
	System.out.println(customer);
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

//fetch:select	单表查询
//lazy:false或者proxy (此时一对多中一方类上的lazy属性的值为false)
public void test2(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();
	
	LinkMan linkMan = session.get(LinkMan.class, 1L); //发送两条select语句,一条查询1号联系人,一条查询1号联系人对应的客户
	Customer customer = linkMan.getCustomer(); //不发送sql
	System.out.println(customer);
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

//fetch:join 多表查询
//lazy:proxy|false 失效,立即加载
public void test3(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();
	
	LinkMan linkMan = session.get(LinkMan.class, 1L); //发送一条迫切左外连接语句直接将关联的对象一起查询了
	Customer customer = linkMan.getCustomer(); //不发送sql
	System.out.println(customer);
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

我们可以简单的总结一下fetch和lazy的作用,其实fetch主要控制抓取关联对象的时候的发送SQL语句的格式的lazy主要控制查询其关联对象的时候是否采用延迟加载

3. 批量抓取

在抓取的策略中有一种叫做批量抓取,就是同时查询多个对象的关联对象的时候,可以采用批量抓取进行优化。如果要实现批量的抓取效果,可以通过配置batch-size来完成。来看下下面的效果。

public void test1(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();
	
	Query query = session.createQuery("from Customer");
	List<Customer> list = query.list();
	for(Customer c : list){
		System.out.println("客户名称:" + c.getCust_name());
		for (LinkMan linkman : c.getLinkMans()) {
			System.out.println("客户对应的联系人的名称:" + linkman.getLkm_name());
		}
	}
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

在没有设置batch-size之前运行以上代码,会出现如下的效果:
在这里插入图片描述
目前数据库中有两个客户,那么会发现执行代码会发生三条SQL语句。那么我们能不能发生一条SQL直接将两个客户的关联的联系人一起查询出来呢,我们可以在<set>上配置batch-size="2"实现优化效果。
在这里插入图片描述
这时会发现SQL语句发生了变化,当然数据量越大,效果越明显。这是在查询所有客户的时候,批量抓取联系人,那么如果我们要实现查询多个联系人的时候,批量抓取联系人所对应的客户对象呢?这时同样需要配置batch-size。但是不是在<many-to-one>上配置,而是在客户一端的<class>上配置batch-size即可。

public void test2(){
	Configuration configuration = new Configuration().configure();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	Session session = sessionFactory.openSession();
	Transaction transaction = session.beginTransaction();
	
	Query query = session.createQuery("from LinkMan");
	List<LinkMan> list = query.list();
	for (LinkMan linkman : list) {
		System.out.println(linkman.getLkm_name());
		System.out.println(linkman.getCustomer().getCust_name());
	}
	
	transaction.commit();
	session.close();
	sessionFactory.close();
}

如果没有配置batch-size执行上述代码会得到如下效果:
在这里插入图片描述
如果配置了batch-size以后,再次执行上述代码得到效果如下,注意:这时候要在客户一端的映射文件中的<class>标签上配置batch-size=“2”。
在这里插入图片描述
这时候可以看到,语句和之前就已经发送了变化了。这些优化都是Hibernate提升自身性能的手段。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值