Hibernate fetch 抓取策略

上一篇文章(Hibernate的延迟加载 ,懒加载,lazy)说到Hibernate的延迟加载跟fetch的配置还有一定关系,下面就来讨论下fetch的用法。

抓取策略(fetch)是指当我们去查询一个对象里面所关联的其他对象时,按照哪种方法去抓取关联对象。

fetch策略常用的有三种:select、subselect、join,下面我们一一介绍。我们还是用上面介绍延迟加载的相关表和实体类。

Company表:

Hibernate fetch 抓取策略_第1张图片

Employee表(employee_company_id为外键)

Hibernate fetch 抓取策略_第2张图片

Company实体类:

import java.util.Set;

public class Company {
    private int companyId;
    private String companyName;
    private Set<Employee> companyEmployees;
    public int getCompanyId() {
        return companyId;
    }
    public void setCompanyId(int companyId) {
        this.companyId = companyId;
    }
    public String getCompanyName() {
        return companyName;
    }
    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }
    public Set<Employee> getCompanyEmployees() {
        return companyEmployees;
    }
    public void setCompanyEmployees(Set<Employee> companyEmployees) {
        this.companyEmployees = companyEmployees;
    }
}

Employee实体类:

public class Employee {
    private int employeeId;
    private String employeeName;
    private Company employeeCompany;
    public int getEmployeeId() {
        return employeeId;
    }
    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }
    public String getEmployeeName() {
        return employeeName;
    }
    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }
    public Company getEmployeeCompany() {
        return employeeCompany;
    }
    public void setEmployeeCompany(Company employeeCompany) {
        this.employeeCompany = employeeCompany;
    }
}

Company hbm配置:

<hibernate-mapping>
    <class name="com.jaeger.hibernatetest.day7.lazy.Company" table="company">
        <id name="companyId" column="company_id">
            <generator class="native"></generator>
     </id>

     <property name="companyName" column="company_name"/>
      <set name="companyEmployees" cascade="all">
           <key column="employee_company_id"></key>
        <one-to-many class="com.jaeger.hibernatetest.day7.lazy.Employee"/>
      </set>
   </class>
</hibernate-mapping>

Employee hbm配置:

<hibernate-mapping>
    <class name="com.jaeger.hibernatetest.day7.lazy.Employee" table="employee">
       <id name="employeeId" column="employee_id">
         <generator class="native"></generator>
     </id>

        <property name="employeeName" column="employee_name"/>
        <many-to-one name="employeeCompany" class="com.jaeger.hibernatetest.day7.lazy.Company" 
            foreign-key="fk_employee_company" column="employee_company_id"
            cascade="save-update">
        </many-to-one>
   </class>
</hibernate-mapping>

fetch属性用在<many-to-one>、<one-to-many>、<many-to-many>、<one-to-one>等关联标签里,我们就用<many-to-one>来介绍,但subselect策略只能用于<one-to-many>和<many-to-many>。从数据库取数据就用get()方法,让外层对象不用延迟加载。


1. select
作用:查询关联对象时,是拿外层对象的外键,向数据库再发送select语句去抓取关联对象。

修改上面Employee hbm的配置:

<many-to-one name="employeeCompany" class="com.jaeger.hibernatetest.day7.lazy.Company" 
            foreign-key="fk_employee_company" column="employee_company_id"
            cascade="save-update" fetch="select">
</many-to-one>

在<many-to-one>标签中我们添加了fetch="select",下面是测试代码:

Employee employee = (Employee)session.get(Employee.class, 1); //A
System.out.println(employee.getEmployeeName());
Company company = employee.getEmployeeCompany(); //B
System.out.println(company.getCompanyName()); //C

A:此处会生成sql语句去查询employee的信息。

select
     employee0_.employee_id as employee1_1_0_,
     employee0_.employee_name as employee2_1_0_,
     employee0_.employee_company_id as employee3_1_0_ 
  from
     employee employee0_ 
 where
     employee0_.employee_id=?

B:此处不会马上去取company的信息,因为<many-to-one>里面默认是lazy="proxy"支持懒加载的。

C:此处会根据employee的外键去查找company的信息,用的就是select fetch策略。

select
     company0_.company_id as company_1_0_0_,
     company0_.company_name as company_2_0_0_ 
  from
     company company0_ 
 where
     company0_.company_id=?

注意:<many-to-one><one-to-many>等关联标签默认是fetch="select"的,所以我们不配置也会采用多条select语句的方式去查询关联对象。


2. join

作用:查询关联对象时,是拿外层对象的外键去join关联对象的表,把关联对象一起取出来。

修改上面Employee hbm的配置:

<many-to-one name="employeeCompany" class="com.jaeger.hibernatetest.day7.lazy.Company" 
            foreign-key="fk_employee_company" column="employee_company_id" cascade="save-update" fetch="join">
</many-to-one>

在<many-to-one>标签中我们换成了fetch="join",下面是测试代码:

Employee employee = (Employee)session.get(Employee.class, 1); //A
System.out.println(employee.getEmployeeName());
Company company = employee.getEmployeeCompany(); //B
System.out.println(company.getCompanyName()); //C

A:此处会生成sql语句去查询employee的信息,并用left join去查询company的信息。

select
     employee0_.employee_id as employee1_1_0_,
     employee0_.employee_name as employee2_1_0_,
     employee0_.employee_company_id as employee3_1_0_,
     company1_.company_id as company_1_0_1_,
     company1_.company_name as company_2_0_1_ 
  from
     employee employee0_ 
  left outer join
     company company1_ 
    on
     employee0_.employee_company_id=company1_.company_id 
 where
     employee0_.employee_id=?

B、C:这两处都不会再生成sql去数据库查询了,因为上面已经通过join拿到了company的信息。

所以使用join策略的话,关联对象的延迟加载就没用了,因为第一个select语句就已经join查询出了关联对象的信息。

3. subselect

作用:

subselect策略只能用于<one-to-many>和<many-to-many>,所以用<one-to-many>来说明。

我们先来看看在默认的select策略下的执行过程。测试代码如下:

List<Company> allCompany = session.createCriteria(Company.class).list(); //A
for(Company company : allCompany){
    for(Employee employee : company.getCompanyEmployees()){ //B
        System.out.println(employee.getEmployeeName());
    }
}

A:发出查询sql语句去查询所有的company信息

select
     this_.company_id as company_1_0_0_,
     this_.company_name as company_2_0_0_ 
  from
     company this_

B:这里会根据company_id去查询关联的employee信息

select
     companyemp0_.employee_company_id as employee3_0_0_,
     companyemp0_.employee_id as employee1_1_0_,
     companyemp0_.employee_id as employee1_1_1_,
     companyemp0_.employee_name as employee2_1_1_,
     companyemp0_.employee_company_id as employee3_1_1_ 
  from
     employee companyemp0_ 
 where
     companyemp0_.employee_company_id=?

上面的sql会重复向数据库发送N次,有多少个company就发送多少次。如果有很多不同的company,这种方式肯定是不合适的,因为会向数据库发送很多重复的sql语句。

下面我们来看看subselect策略,先修改Company hbm配置:

<set name="companyEmployees" cascade="all" fetch="subselect">
    <key column="employee_company_id"></key>
    <one-to-many class="com.jaeger.hibernatetest.day7.lazy.Employee" />
</set>

测试代码跟上面一样:

List<Company> allCompany = session.createCriteria(Company.class).list(); //A
for(Company company : allCompany){
    for(Employee employee : company.getCompanyEmployees()){ //B
        System.out.println(employee.getEmployeeName());
    }
}

A:发出查询sql语句去查询所有的company信息

select
     this_.company_id as company_1_0_0_,
     this_.company_name as company_2_0_0_ 
  from
     company this_

B:这里会用一个select子查询去查询所有的employee信息

select
     companyemp0_.employee_company_id as employee3_0_1_,
     companyemp0_.employee_id as employee1_1_1_,
     companyemp0_.employee_id as employee1_1_0_,
     companyemp0_.employee_name as employee2_1_0_,
     companyemp0_.employee_company_id as employee3_1_0_ 
  from
     employee companyemp0_ 
 where
     companyemp0_.employee_company_id in (
         select
              this_.company_id 
           from
              company this_
     )

上面的sql只会向数据库发一次,因为所有的employee都已经取出来了。

对于对于set来说,如果用于遍历,则使用subselect,这样可以一次性把所有数据取出来;如果只取set里几条数据就用默认的select,这样虽然会发送多次sql,但查询量较少。

你可能感兴趣的:(Hibernate,fetch,抓取策略)