【Hibernate 8】Hibernate的调优方法:抓取策略

在上一篇博客中,介绍了Hibernate的缓存机制。合理的配置缓存,可以极大程度上优化Hibernate的性能。这篇博客,介绍另外一个调优方式:抓取策略。

一、什么是抓取策略

抓取策略(fetching strategy):当应用程序需要在关联关系间进行导航的时候,Hibernate如何获取关联对象的策略。抓取策略可以在O / R映射的元数据中声明,也可以在特定的HQL或条件查询(Criteria Query)中重载声明。

二、Hibernate的抓取策略

在Hibernate中,主要包括四种抓取策略:

2.1,连接抓取(Join fetching)

Hibernate通过在select语句中使用join等关键字,来获得对象的关联实例或者关联集合

2.2,查询抓取(Select fetching)

Hibernate通过发送另外一条select语句抓取当前对象的关联实体或集合。除非是显示的指定lazy=“false”禁止延迟抓取,否则只有当你真正访问关联关系的时候,才会执行第二条select语句。

2.3,子查询抓取(Subselect fetching)

Hibernate通过另外发送一条SELECT 语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合。除非你显式的指定lazy="false" 禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。

2.4,批量抓取(Batch fetching)

是Hibernate对查询抓取的优化方案, 通过指定一个主键或外键列表,Hibernate使用单条SELECT语句获取一批对象实例或集合。

二、详解Hibernate的抓取策略

首先,在Hibernate.cfg.xml中配置抓取策略的数据量(非必须,比如批量更新,可以在用到批量更新的时候配置):

<span style="font-family:KaiTi_GB2312;font-size:18px;"> <!-- 批量更新配置 -->
<property name="hibernate.jdbc.batch_size">30</property>

 <!-- 批量抓取参数的配置 -->
<property name="hibernate.jdbc.fetch_size">50</property></span>

2.1,连接抓取

<span style="font-family:KaiTi_GB2312;font-size:18px;"><many-to-one name="classes" column="classesid" fetch="join" not-null="true"/></span>

fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合,不过,此时的lazy失效

测试方法代码如查询抓取中的testFetch1()。

控制台打印:

Hibernate:
select student0_.id as id0_0_, classes1_.id as id1_1_, student0_.name as name0_0_, student0_.classesid as classesid0_0_, classes1_.name as name1_1_
from t_student student0_ inner join t_classes classes1_ on student0_.classesid=classes1_.id
where student0_.id=1

student.name=班级0的学生0

classes.name=班级0

2.2,查询抓取

<span style="font-family:KaiTi_GB2312;font-size:18px;"><many-to-one name="classes" column="classesid" fetch="select"/></span>

fetch="select",另外发送一条select语句抓取当前对象关联实体或集合

<span style="font-family:KaiTi_GB2312;font-size:18px;">public void testFetch1() {
	Session session = null;
	try {
		session = HibernateUtils.getSession();
		session.beginTransaction();

		Student student = (Student)session.load(Student.class, 1);
		System.out.println("student.name=" + student.getName());

		Classes classes = student.getClasses();
		System.out.println("classes.name=" + classes.getName());

		session.getTransaction().commit();
	}catch(Exception e) {
		e.printStackTrace();
		session.getTransaction().rollback();
	}finally {
		HibernateUtils.closeSession(session);
	}
}</span>

控制台打印:

Hibernate:
select student0_.id as id0_0_, student0_.name as name0_0_, student0_.classesid as classesid0_0_ from t_student student0_
where student0_.id=?

student.name=班级0的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_
from t_classes classes0_
where classes0_.id=?

classes.name=班级0

默认的启用了延迟加载,所以,仅仅当执行Classes classes=student.getClasses()时,才发出了另一条select语句。

2.3,子查询抓取

<span style="font-family:KaiTi_GB2312;font-size:18px;"><set name="students" order-by="id" inverse="true" cascade="all" fetch="subselect">
	<key column="classesid"/>
	<one-to-many class="com.bjpowernode.hibernate.Student"/>
</set></span>

fetch="subselect",另外发送一条select语句抓取在前面查询到的所有实体对象的关联集合

<span style="font-family:KaiTi_GB2312;font-size:18px;">public void testFetch1() {
	Session session = null;
	try {
		session = HibernateUtils.getSession();
		session.beginTransaction();

		List classesList = session.createQuery("select c from Classes c where c.id in(1, 2, 3)").list();
		for (Iterator iter1 = classesList.iterator(); iter1.hasNext();) {
			Classes classes = (Classes) iter1.next();
			System.out.println(classes.getName());
			for (Iterator iter = classes.getStudents().iterator(); iter.hasNext();) {
				Student student = (Student) iter.next();
				System.out.println(student.getName());
			}
		}
		session.getTransaction().commit();
	} catch (Exception e) {
		e.printStackTrace();
		session.getTransaction().rollback();
	} finally {
		HibernateUtils.closeSession(session);
	}
}</span>

控制台打印:

Hibernate: select classes0_.id as id1_, classes0_.name as name1_ from t_classes classes0_ where classes0_.id in (1 , 2 , 3)

班级0

Hibernate: select students0_.classesid as classesid1_, students0_.id as id1_, students0_.id as id0_0_, students0_.name as name0_0_, students0_.classesid as classesid0_0_
from t_student students0_
where students0_.classesid in (select classes0_.id from t_classes classes0_
where classes0_.id in (1 , 2 , 3)) order by students0_.id

班级0的学生0

班级0的学生1

班级0的学生2

班级0的学生3

班级0的学生4

班级0的学生5

班级0的学生6

班级0的学生7

班级0的学生8

班级0的学生9

班级1

班级1的学生0

班级1的学生1

班级1的学生2

班级1的学生3

班级1的学生4

班级1的学生5

班级1的学生6

班级1的学生7

班级1的学生8

班级1的学生9

班级2

班级2的学生0

班级2的学生1

班级2的学生2

班级2的学生3

班级2的学生4

班级2的学生5

班级2的学生6

班级2的学生7

班级2的学生8

班级2的学生9

2.4,批量抓取

单端代理的批量抓取:Hibernate可以充分有效的使用批量抓取,也就是说,如果仅一个访问代理(或集合),那么Hibernate将不载入其他未实例化的代理。 批量抓取是延迟查询抓取的优化方案,你可以在两种批量抓取方案之间进行选择:在类级别和集合级别。

如果:你在一个Session中载入了25个 Cat实例,每个Cat实例都拥有一个引用成员owner, 其指向Person,而Person类是代理,同时lazy="true"。 如果你必须遍历整个cats集合,对每个元素调用getOwner()方法,Hibernate将会默认的执行25次SELECT查询, 得到其owner的代理对象。这时,你可以通过在映射文件的Person属性,显式声明batch-size,改变其行为:

<class name="Person" batch-size="10">...</class>随之,Hibernate将只需要执行三次查询,分别为10、10、 5。

实例分析:

<span style="font-family:KaiTi_GB2312;font-size:18px;"><class name="com.angel.hibernate.Classes" table="t_classes" batch-size="10"></span>

测试类:

<span style="font-family:KaiTi_GB2312;font-size:18px;">public void testFetch1() {
	Session session = null;
	try {
		session = HibernateUtils.getSession();
		session.beginTransaction();
		List students = session
				.createQuery("select s from Student s  where s.id in(:ids)")
				.setParameterList("ids",new Object[] { 1, 11, 21, 31, 41, 51, 61, 71, 81,91 }).list();
		for (Iterator iter = students.iterator(); iter.hasNext();) {
			Student student = (Student) iter.next();
			System.out.println(student.getName());
			System.out.println(student.getClasses().getName());
		}
		session.getTransaction().commit();
	} catch (Exception e) {
		e.printStackTrace();
		session.getTransaction().rollback();
	} finally {
		HibernateUtils.closeSession(session);
	}
}</span>

控制台输出结果:

Hibernate: select student0_.id as id0_, student0_.name as name0_, student0_.classesid as classesid0_ from t_student student0_ where student0_.id in (? , ? , ? , ? , ? , ? , ? , ? , ? , ?)

班级0的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

班级0

班级1的学生0

班级1

班级2的学生0

班级2

班级3的学生0

班级3

班级4的学生0

班级4

班级5的学生0

班级5

班级6的学生0

班级6

班级7的学生0

班级7

班级8的学生0

班级8

班级9的学生0

班级9

如果不配置批量抓取batch-size="10",那么控制台打印的结果为(出现了N+1问题,所以,配置批量抓取,也可以解决N+1问题):

Hibernate: select student0_.id as id0_, student0_.name as name0_, student0_.classesid as classesid0_ from t_student student0_ where student0_.id in (? , ? , ? , ? , ? , ? , ? , ? , ? , ?)

班级0的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?

班级0

班级1的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?

班级1

班级2的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?

班级2

班级3的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?

班级3

班级4的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?

班级4

班级5的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?

班级5

班级6的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?

班级6

班级7的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?

班级7

班级8的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?

班级8

班级9的学生0

Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?

班级9

三、总结

抓取策略和缓存机制一样,都是Hibernate调优的重要手段:我们通过Hibernate的抓取策略,减少发出sql的次数,将其数据存入缓存,再次减少开合数据的次数。但并不是每一个应用程序都必须用上这些东西。具体的使用,还需要根据业务的需求,酌情而定。下面一篇博客,将介绍锁的概念:悲观锁和乐观锁

时间: 2024-10-11 13:40:53

【Hibernate 8】Hibernate的调优方法:抓取策略的相关文章

【Java EE 学习第48天】【Hibernate学习第五天】【抓取策略】【二级缓存】【HQL】

一.抓取策略. 1.hibernate中提供了三种抓取策略. (1)连接抓取(Join Fetch):这种抓取方式是默认的抓取方式.使用这种抓取方式hibernate会在select中内连接的方式获取对象的关联对象或者关联集合. (2)查询抓取(select Fetch):这种抓取方式会另外发送一条select语句抓取当前对象的关联实体或者集合.除非指定lazy=false,否则只有在真正访问关联关系的时候才会执行第二条select语句. (3)子查询抓取(subselect Fetch):另外

hibernate抓取策略学习

一.hibernate抓取策略概述 Hibernate抓取策略(fetching strategy)是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略.抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL 或条件查询(Criteria Query)中重载声明. 需要注意的是:hibernate的抓取策略只影响get load 方法,对hql是不影响的. 二.hibernate 抓取策略分类 hibernate有如下

Hibernate fetch 抓取策略

上一篇文章(Hibernate的延迟加载 ,懒加载,lazy)说到Hibernate的延迟加载跟fetch的配置还有一定关系,下面就来讨论下fetch的用法. 抓取策略(fetch)是指当我们去查询一个对象里面所关联的其他对象时,按照哪种方法去抓取关联对象. fetch策略一共有四种:select.subselect.join.batch,下面我们一一介绍.我们还是用上面介绍延迟加载的相关表和实体类. Company表: Employee表(employee_company_id为外键) Com

hibernate的延迟加载和抓取策略

一,延迟加载 1.实体类延迟加载 通过代理机制完成,由javassist类库实现运行时代理,修改实体类的字节码实现了运行时代理     <class lazy="true|false">     实体级别的延迟加载默认值为true,意味实体对象是延迟加载,只影响load方法.      <class lazy="true|false">其他查询方式都是立即加载              2.关联属性延迟加载 默认情况下除了<one-to

(转)hibernate 延迟加载和抓取策略

一.延迟加载 1.简单查询get,load 针对对象本身延迟或即时 当使用load方法来得到一个对象时,此时hibernate会使用延迟加载的机制来加载这个对象,即:当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这个时候才会发出sql语句,从数据库中去查询我们的对象. 相对于load的延迟加载方式,get就直接的多,当我们使用session

Hibernate学习笔记(八) — 懒加载与抓取策略

懒加载(Load On Demand)是一种独特而又强大的数据获取方法,它能够在用户滚动页面的时候自动获取更多的数据,而新得到的数据不会影响原有数据的显示,同时最大程度上减少服务器端的资源耗用.总结一句话:什么时候需要数据,什么时候加载. 一.懒加载 1.1 类的懒加载 由javassist产生的代理类与Classes类是继承关系, session.load()方法产生的是代理对象,该代理类是持久化类的子类 /** * 类的懒加载 */ @Test public void testClass_l

【Hibernate学习】 —— 抓取策略(注解方式)

当应用程序需要在关联关系间进行导航的时候,hibernate如何获取关联对象的策略. 抓取策略的方式: FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载. FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载. 在项目中的体现,我这里有两个实体,一个放基础数据的实体,另一个是放操作记录的实体.这两个实体的关系是一对一关系,因此要把基础数据中的主键放在操作记录中作为外键,查询的时候查询操作记录表顺便把基础表相关信息

hibernate 延迟加载和抓取策略

一.延迟加载 1.简单查询get,load 针对对象本身延迟或即时 当使用load方法来得到一个对象时,此时hibernate会使用延迟加载的机制来加载这个对象,即:当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这个时候才会发出sql语句,从数据库中去查询我们的对象. 相对于load的延迟加载方式,get就直接的多,当我们使用session

029 hibernate抓取策略

实例A引用实例B,B如果是代理的话(比如多对一关联中):如果遍历A的查询结果集(假设有10条记录),在遍历A的时候,访问B变量,将会导致n次查询语句的发出!这个时候,如果在B一端的class上配置batch-size,hibernate将会减少SQL语句的数量. Hibernate可以充分有效的使用批量抓取,也就是说,如果仅一个访问代理(或集合),那么hibernate将不载入其他未实例化代理.批量抓取是延迟查询抓取的优化方案,你可以在两种批量抓取方案之间进行选择:在类级别和集合级别. 类/实体