Hibernate延迟加载有很多配置方法,本文主要说明几种常用的配置方法,以及在Session的get()和load()方法中的效果。下面是本例用到的两张表、实体类和配置信息:

Company表:

wKioL1bVHaPArE8EAAAN7mNGBzQ433.png

Employee表(employee_company_id为外键)

wKioL1bVHaPDb5pwAAAbt64pCpc595.png

Company实体类:

import java.util.Set;public class Company {    private int companyId;    private String companyName;    private Set
 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
 getCompanyEmployees() {        return companyEmployees;    }    public void setCompanyEmployees(Set
 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配置:

    
        
            
          
      
           
        
         

Employee hbm配置

    
       
         
             
        
           

1. 从最简单的get()和load()方法开始说明

首先来看get()方法

Employee employee = (Employee)session.get(Employee.class, 1); //ASystem.out.println(employee.getEmployeeName()); //BCompany company = employee.getEmployeeCompany(); //CSystem.out.println(company.getCompanyName()); //D

A:当代码走到此处,Hibernate就会立刻发出sql去数据库里查询(前提是不存在缓存,否则会优先取缓存里的数据。下面所有讨论都是在默认不存在缓存情况下进行的)。sql如下:

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=?

C:这里不会再次发出查询语句去查询Company的信息,产生的company对象其实是一个代理对象。

D:这里才真正发出查询语句去查询Company的信息。sql如下:

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

所以get()方法不支持延迟加载,在get时就会去数据库查询,但只是针对get要去取的对象,该对象里面所引用的对象则需要其他方式来控制,默认是延迟加载的。下面会有详细说明。

再来看load()方法

Employee employee = (Employee)session.load(Employee.class, 1); //ASystem.out.println(employee.getEmployeeName()); //BCompany company = employee.getEmployeeCompany(); //CSystem.out.println(company.getCompanyName()); //D

A:当代码走到此处,Hibernate不会马上发出sql去数据库里查询,此时的employee只是Hibernate给我们生成的一个代理对象。

B:这时Hibernate才会向数据库发出查询语句,也就是说load出来的对象只有在真正使用时才会发出sql。两条sql跟上面一样。

C:这里不会再次发出查询语句去查询Company的信息,产生的company对象其实是一个代理对象。

D:这里才真正发出查询语句去查询Company的信息。sql如下:

load()方法还有一点特殊的地方,那就是去查询主键时

get()方法

Employee employee = (Employee)session.get(Employee.class, 1);System.out.println(employee.getEmployeeId());

load()方法

Employee employee = (Employee)session.load(Employee.class, 1);System.out.println(employee.getEmployeeId());

get()方法会发出sql语句,但load()方法却不会,因为load()方法生成的是代理对象,而该对象的Id已经在查询前设置进去了,所以不会再去数据库查询。

2.Class的lazy属性

class默认是支持延迟加载的,也就是说在class上不配置lazy属性,默认是lazy="true"的,所以load()方法才会延迟加载。如果我们在hbm文件或者annotation中去把他设置为lazy="false",如下:

这时候get()和load()方法效果都一样了,load()不会产生延迟加载的效果,而是一开始就发出sql查询。

有一点需要注意的地方,那就是当一个类里面所引用的类的class配置为lazy="false"时(比如Employee类里面的Company),我们把Company hbm配置改为:

再把Employee hbm配置改为

这个时候Employee类支持延迟加载,而Company类不支持。我们再运行上面的get()和load()方法,get()方法还是一开始就发出sql查询,load()方法还是在employee被使用时发出sql查询。但不同的地方在于,Company类不支持延迟加载,所以在查询Employee时就会以left outer join的方式把company的信息一起查询出来,而不是像以前单独发sql查询去取Company的数据。sql如下:

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=?

3.many-to-one、one-to-one和many-to-many中的lazy属性

上面我们说到对象里面所引用的对象需要其他方式来控制,一种是直接配置引用对象的class里面的lazy属性,在第2点中已经演示过。当lazy="false"时Hibernate会采用left outer join的方式取出引用对象的数据。

另外一种就是配置映射标签的lazy属性,当lazy="proxy"时(相当于class的lazy="true"),则会产生第1点中的延迟加载效果,在使用引用对象时才发出sql请求,这也就解释了为什么生成的是代理对象。Employee hbm配置如下:

当lazy="false"时,则会在查询Employee时同时用Employee的外键发出sql去查询Company的数据。跟配置class不同的是,映射标签配置会生成第1点中的两条sql语句,而不是用left outer join去连接两张表。

注意:如果同时配置了class标签和映射标签的lazy属性,则class标签优先。

4.one-to-many中Set、List、Map的lazy

①在one-to-many的关系中,one端的Set、List、Map都可以设置lazy属性,在此我们就用Set来说明。在上面Company hbm配置中已经配置了Company所对应的Employee的Set集合,但没有设置lazy属性,因为对于Set、List、Map来说,默认为lazy="true"。

下面是测试代码:

Company company = (Company)session.load(Company.class, 3); //ASet
 employees = company.getCompanyEmployees(); //Bfor(Employee employee : employees){ //C    System.out.println(employee.getEmployeeName());}

A:此处不会发出sql语句,因为load默认是延迟加载的。

B:此处就会发出sql语句,但只会查询出Company的相关信息,sql如下:

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

C:此处开始发出sql语句,根据company_id去查询其关联的所有Employee,sql如下:

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=?

当lazy="false"时,在B处就会同时发出上面两条sql查询语句,XML配置为:

    
    

②此处还有一点值得注意的地方,那就是当我们去获得Set、List、Map所含元素的个数的时候,最好使用lazy="extra"来代替lazy="true"。下面是说明代码:

Company company = (Company)session.load(Company.class, 3);Set
 employees = company.getCompanyEmployees();System.out.println(employees.size()); //A

当Set的lazy="true"时,Hibernate会发出如下sql来查询size:

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=?

当Set的lazy="extra"时,Hibernate会发出如下sql来查询size:

select   count(employee_id)  from   employee  where   employee_company_id =?

所以当数据量很大的时候,应当使用lazy="extra"来延迟加载。

注意:class的lazy配置并不会对Set、List、Map的延迟加载产生影响。

5.fetch的配置也会影响到延迟加载的效果,可以参考这篇文章:。