Hibernate的抓取策略总结

今天在做hibernate的性能优化的时候,hibernate的抓取策略又派上用场了,于是顺便总结了一下并作了如下记录。hibernate的抓取策略通常有下面四种:

     1.连接抓取(Join fetching) ,即Hibernate通过 在select语句使用outer join(外连接)来 获得对象的关联实例或  者关联集合.

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

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

select语句 .

      4.批量抓取(Batch fetching) ,这种策略跟查询抓取本质上是一样的,只不过是 对查询抓取的优化而已, 通过指定一个主键或外键 列表,Hibernate使用单条select语句获取一批对象实例或集合.

 Join fetching , Select fetching 与 Batch-size 可以为单个实体的抓取进 行性能优化;

Joinfetching,Selectfetching,Subselectfetching,Batchfetching都可以为集合的抓取进行性能优化;

下面将以一个多对一双向关联映射(多个员工属于同一部门)进行说明:

关联映射后的关系模型:

t_employee(idintpk,namevarchar,departmentidintfk->t_department(id))

t_department(idintpk,namevarchar)

实体模型:

com.lrh.hibernate.Employee(intid,Stringname,Departmentdepartment)

com.lrh.hibernate.Department(intid,Stringname,Setemployees)

配置文件:

<!--com.lrh.hibernate/Employee.hbm.xml-->

<hibernate-mapping>

<classname="com.lrh.hibernate.Employee"table="t_employee">

<idname="id"><generatorclass="native"/></id>

<propertyname="name"/>

<!--<many-to-one>映射多对一关系-->

<many-to-onename="department"column="departmentid"/>

</class>

</hibernate-mapping>

<!--com.lrh.hibernate/Department.hbm.xml-->

<hibernate-mapping>

<classname="com.lrh.hibernate.Department"table="t_department">

<idname="id"><generatorclass="native"/></id>

<propertyname="name"/>

<!--双向关联中,为<set>加入”inverse=true”可以反转维护关系:

Hibernate将放弃从一的一端维护。也就是employee和department的关系必须使用employee维护,

操作department时Hibernate将不维护这个关系。

-->

<setname="employees"inverse="true">

<!--<key>指定引用至自身的外键表(t_employee)中的外键-->

<keycolumn="departmentid"/>

<!--<one-to-many>映射一对多关系-->

<one-to-manyclass="com.lrh.hibernate.Employee"/>

</set>

</class>

  </hibernate-mapping>

1.连接抓取(Join fetching)

     连接抓取, 使用连接抓取可以将本来需要查询两次(或多次)表的多次查询 ,现在只需要一次查询即可完成,以上面的关系模型为例,在初始化一个含有一对多关系的 Department 与Employee 的时候, 会先加载Department , 然后再根据Department.id 将对应的Employee 集合初始化, 一般完成初始化需要发送至少两条 SQL 语句, 但如果用 连接抓取, 则会根据需要查询的Department.id, 将 Department 表与 Employee 表连接起来进行查询,仅仅一条 SQL 语句就可以将需要的数据全部加载出来;

使用连接抓取的com.lrh.hibernate/Department.hbm.xml配置文件修改<set></set>节点如下:

<!--添加fetch="join"-->

<setname="employees"inverse="true"fetch="join">

<keycolumn="departmentid"/>

<one-to-manyclass="com.lrh.hibernate.Employee"/>

</set>

测试核心代码:

Departmentd=(Department)session.get(Department.class,1);

     System.out.println(d.getEmployees.size());

2.查询抓取(Selectfetching)

查询抓取是在集合抓取的时候使用的默认策略,即如果集合需要初始化,那么会重新发出一条SQL语句初始化Employee集合;也就是常说的N+1次查询的查询策略;

使用连接抓取的com.lrh.hibernate/Department.hbm.xml配置文件修改<set></set>节点如下:

<!--添加fetch="select"-->

<setname="employees"inverse="true"fetch="select">

<keycolumn="departmentid"/>

<one-to-manyclass="com.lrh.hibernate.Employee"/>

</set>

测试核心代码:

Departmentd=(Department)session.get(Department.class,1);

System.out.println(d.getEmployees.size());

   

3.子查询抓取(Subselect fetching)

           子查询抓取, 另外发送一条SELECT 语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合. 还是以上面的关系模型例子 : 如果查询出了3 个 Department 实体,由于开启了懒加载,那么他们的 Employee 都没有被初始化, 那么我现在手动初始化一个Department 的 Employee ,此时由于我选的是 Subselect fetching策略,所以 Hibernate 会将前面查询到的实体对象(3 个 Department)的关联集合(在 <set name="Employee" fetch="subselect" /> )使用一条 Select 语句一次性抓取出来, 这样减少了与数据库的交互次数, 一次将每个对象的集合都给初始化了;subselect 只在 <set> 集合中出现 。

使用连接抓取的com.lrh.hibernate/Department.hbm.xml配置文件修改<set></set>节点如下:

<!--添加fetch="subselect"-->

<setname="employees"inverse="true"fetch="subselect">

<keycolumn="departmentid"/>

<one-to-manyclass="com.lrh.hibernate.Employee"/>

</set>

测试核心代码:

Listresults=session.createQuery("FromDepartmentdwhered.idin(1,2,3)").list();

Departmentd=(Department)results.get(0);

     System.out.println(d.getEmployees.size());

4.批量抓取(Batch fetching)           批量抓取跟查询抓取本质上是一样的,只不过将一次一条的 查询策略改为一次 N 条的批量 查询; 同样以上面的关系模型为例,我查询出了 3个 Department 实体,Employee 默认开启了懒加载,我们可以如此设置 :

<set name="Employee" fetch="select" batch-size="2" />  当初始化一个 Department 的 Employee 集合的时候, Hibernate 还是发出了一条SQL 语句,跟查询抓取不同的是:这条 SQL 与是通过指定了 Employee 表中的 departmentid 外键列表(2个),Hibernate 会以一条 SQL 语句初始化 batch-size 指定的数量的 Employee 集合;即根据batch-size 分成了两组(一组有2个 Department id 值的列表,第二组只有1个)来初始化 Employee 集合。

使用连接抓取的com.lrh.hibernate/Department.hbm.xml配置文件修改<set></set>节点如下:

<!--添加fetch="select"batch-size="2"-->

<setname="employees"inverse="true"fetch="select"batch-size="2">

<keycolumn="departmentid"/>

<one-to-manyclass="com.lrh.hibernate.Employee"/>

</set>

测试核心代码:

Listresults=session.createQuery("FromDepartmentdwhered.idin(1,2,3)").list();

Departmentd=(Department)results.get(0);

     System.out.println(d.getEmployees.size());

相关推荐