设为首页 收藏本站
查看: 296|回复: 0

[经验分享] Hibernate的延迟加载 ,懒加载,lazy

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2016-3-4 08:57:10 | 显示全部楼层 |阅读模式
Hibernate延迟加载有很多配置方法,本文主要说明几种常用的配置方法,以及在Session的get()和load()方法,Query对象的iterator()和list()方法中的效果。下面是本例用到的两张表、实体类和配置信息:

Company表:
wKioL1bVHaPArE8EAAAN7mNGBzQ433.jpg
Employee表(employee_company_id为外键)
wKioL1bVHaPDb5pwAAAbt64pCpc595.jpg
Company实体类:
1
<span style="font-family:'楷体', '楷体_GB2312', SimKai;">import java.util.Set;<br><br>public class Company {<br>    private int companyId;<br>    private String companyName;<br>    private Set<Employee> companyEmployees;<br>    public int getCompanyId() {<br>        return companyId;<br>    }<br>    public void setCompanyId(int companyId) {<br>        this.companyId = companyId;<br>    }<br>    public String getCompanyName() {<br>        return companyName;<br>    }<br>    public void setCompanyName(String companyName) {<br>        this.companyName = companyName;<br>    }<br>    public Set<Employee> getCompanyEmployees() {<br>        return companyEmployees;<br>    }<br>    public void setCompanyEmployees(Set<Employee> companyEmployees) {<br>        this.companyEmployees = companyEmployees;<br>    }<br>}</span><br>



Employee实体类:

1
<span style="font-family:'楷体', '楷体_GB2312', SimKai;">public class Employee {<br>    private int employeeId;<br>    private String employeeName;<br>    private Company employeeCompany;<br>    public int getEmployeeId() {<br>        return employeeId;<br>    }<br>    public void setEmployeeId(int employeeId) {<br>        this.employeeId = employeeId;<br>    }<br>    public String getEmployeeName() {<br>        return employeeName;<br>    }<br>    public void setEmployeeName(String employeeName) {<br>        this.employeeName = employeeName;<br>    }<br>    public Company getEmployeeCompany() {<br>        return employeeCompany;<br>    }<br>    public void setEmployeeCompany(Company employeeCompany) {<br>        this.employeeCompany = employeeCompany;<br>    }<br>}</span><br>



Company hbm配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
<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配置:
1
2
3
4
5
6
7
8
9
10
11
12
<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>




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

首先来看get()方法
1
2
3
4
Employee employee = (Employee)session.get(Employee.class, 1); //A
System.out.println(employee.getEmployeeName()); //B
Company company = employee.getEmployeeCompany(); //C
System.out.println(company.getCompanyName()); //D



A:当代码走到此处,Hibernate就会立刻发出sql去数据库里查询(前提是不存在缓存,否则会优先取缓存里的数据。下面所有讨论都是在默认不存在缓存情况下进行的)。sql如下:
1
2
3
4
5
6
7
8
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如下:
1
2
3
4
5
6
7
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()方法
1
2
3
4
Employee employee = (Employee)session.load(Employee.class, 1); //A
System.out.println(employee.getEmployeeName()); //B
Company company = employee.getEmployeeCompany(); //C
System.out.println(company.getCompanyName()); //D



A:当代码走到此处,Hibernate不会马上发出sql去数据库里查询,此时的employee只是Hibernate给我们生成的一个代理对象。
B:这时Hibernate才会向数据库发出查询语句,也就是说load出来的对象只有在真正使用时才会发出sql。两条sql跟上面一样。
C:这里不会再次发出查询语句去查询Company的信息,产生的company对象其实是一个代理对象。

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

load()方法还有一点特殊的地方,那就是去查询主键时
get()方法
1
2
Employee employee = (Employee)session.get(Employee.class, 1);
System.out.println(employee.getEmployeeId());



load()方法
1
2
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",如下:
1
<class name="com.jaeger.hibernatetest.day7.lazy.Employee" table="employee" lazy="false">



这时候get()和load()方法效果都一样了,load()不会产生延迟加载的效果,而是一开始就发出sql查询。
有一点需要注意的地方,那就是当一个类里面所引用的类的class配置为lazy="false"时(比如Employee类里面的Company),我们把Company hbm配置改为:
1
<class name="com.jaeger.hibernatetest.day7.lazy.Company" table="company" lazy="false">



再把Employee hbm配置改为:
1
<class name="com.jaeger.hibernatetest.day7.lazy.Employee" table="employee" lazy="true">



这个时候Employee类支持延迟加载,而Company类不支持。我们再运行上面的get()和load()方法,get()方法还是一开始就发出sql查询,load()方法还是在employee被使用时发出sql查询。但不同的地方在于,Company类不支持延迟加载,所以在查询Employee时就会以left outer join的方式把company的信息一起查询出来,而不是像以前单独发sql查询去取Company的数据。sql如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
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配置如下:

1
2
3
4
<many-to-one name="employeeCompany" class="com.jaeger.hibernatetest.day7.lazy.Company"
            foreign-key="fk_employee_company" column="employee_company_id"
            cascade="save-update" lazy="proxy">
</many-to-one>



当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"。
下面是测试代码:
1
2
3
4
5
Company company = (Company)session.load(Company.class, 3); //A
Set<Employee> employees = company.getCompanyEmployees(); //B
for(Employee employee : employees){ //C
    System.out.println(employee.getEmployeeName());
}



A:此处不会发出sql语句,因为load默认是延迟加载的。
B:此处就会发出sql语句,但只会查询出Company的相关信息,sql如下:
1
2
3
4
5
6
7
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如下:
1
2
3
4
5
6
7
8
9
10
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配置为:
1
2
3
4
<set name="companyEmployees" cascade="all" lazy="false">
    <key column="employee_company_id"></key>
    <one-to-many class="com.jaeger.hibernatetest.day7.lazy.Employee" />
</set>



②此处还有一点值得注意的地方,那就是当我们去获得Set、List、Map所含元素的个数的时候,最好使用lazy="extra"来代替lazy="true"。下面是说明代码:
1
2
3
Company company = (Company)session.load(Company.class, 3);
Set<Employee> employees = company.getCompanyEmployees();
System.out.println(employees.size()); //A



当Set的lazy="true"时,Hibernate会发出如下sql来查询size:
1
2
3
4
5
6
7
8
9
10
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:
1
2
3
4
5
6
select
   count(employee_id)
from
   employee
where
   employee_company_id =?



所以当数据量很大的时候,应当使用lazy="extra"来延迟加载。
注意:class的lazy配置并不会对Set、List、Map的延迟加载产生影响。

5.fetch的配置也会影响到延迟加载的效果,我们会在关于fetch文章里加以说明。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-186104-1-1.html 上篇帖子: Linux文字和图形界面切换 下篇帖子: Linux设备模型<一>认识Sysfs
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表