zoukankan      html  css  js  c++  java
  • hibernate缓存机制详细介绍

    hibernate的缓存机制,包括一级缓存(session级别)、二级缓存(sessionFactory级别)。

    一:hibernate的 N+1问题

    list()获得对象:

    如果通过list()方法来获得对象,毫无疑问,hibernate会发出一条sql语句,将所有的对象查询出来,这点相信大家都能理解

    Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.rid as rid2_, student0_.sex as sex2_ from t_student student0_ limit ?

    那么,我们再来看看iterator()这种情况

    iterator()获得对象

    在执行完上述的测试用例后,我们来看看控制台的输出,看会发出多少条 sql 语句:

    我们看到,当如果通过iterator()方法来获得我们对象的时候,hibernate首先会发出1条sql去查询出所有对象的 id 值,当我们如果需要查询到某个对象的具体信息的时候,hibernate此时会根据查询出来的 id 值再发sql语句去从数据库中查询对象的信息,这就是典型的 N+1 的问题。

    那么这种 N+1 问题我们如何解决呢,其实我们只需要使用 list() 方法来获得对象即可。但是既然可以通过 list() 我们就不会出现 N+1的问题,那么我们为什么还要保留 iterator()这种形式呢?我们考虑这样一种情况,如果我们需要在一个session当中要两次查询出很多对象,此时我们如果写两条 list()时,hibernate此时会发出两条 sql 语句,而且这两条语句是一样的,但是我们如果第一条语句使用 list(),而第二条语句使用 iterator()的话,此时我们也会发两条sql语句,但是第二条语句只会将查询出对象的id,所以相对应取出所有的对象而已,显然这样可以节省内 存,而如果再要获取对象的时候,因为第一条语句已经将对象都查询出来了,此时会将对象保存到session的一级缓存中去,所以再次查询时,就会首先去缓 存中查找,如果找到,则不发sql语句了。这里就牵涉到了接下来这个概念:hibernate的一级缓存。

    二、一级缓存(session级别)

    我们来看看hibernate提供的一级缓存:

    我们来看看控制台输出:

    Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.rid as rid2_,
    student0_.sex as sex2_ from t_student student0_ limit ?

    我们看到此时hibernate仅仅只会发出一条 sql 语句,因为第一行代码就会将整个的对象查询出来,放到session的一级缓存中去,当我如果需要再次查询学生对象时,此时首先会去缓存中看是否存在该对 象,如果存在,则直接从缓存中取出,就不会再发sql了,但是要注意一点:hibernate的一级缓存是session级别的,所以如果session关闭后,缓存就没了,此时就会再次发sql去查数据库

    Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?

    我们看到此时会发出两条sql语句,因为session关闭以后,一级缓存就不存在了,所以如果再查询的时候,就会再发sql。要解决这种问题,我们应该怎么做呢?这就要我们来配置hibernate的二级缓存了,也就是sessionFactory级别的缓存。

    三、二级缓存(sessionFactory级别)

    使用hibernate二级缓存,我们首先需要对其进行配置,配置步骤如下:

    1.hibernate并没有提供相应的二级缓存的组件,所以需要加入额外的二级缓存包,常用的二级缓存包是EHcache。这个我们在下载好的 hibernate的lib->optional->ehcache下可以找到(我这里使用的hibernate4.1.7版本),然后将里 面的几个jar包导入即可。

    2.在hibernate.cfg.xml配置文件中配置我们二级缓存的一些属性:

    我这里使用的是hibernate4.1.7版本,如果是使用hibernate3的版本的话,那么二级缓存的提供类则要配置成这个:

    3.配置hibernate的二级缓存是通过使用 ehcache的缓存包,所以我们需要创建一个 ehcache.xml 的配置文件,来配置我们的缓存信息,将其放到项目根目录下

    这样我们的二级缓存配置就算完成了,接下来我们来通过测试用例测试下我们的二级缓存是否起作用

    ①二级缓存是sessionFactory级别的缓存

    TestCase1:

    因为二级缓存是sessionFactory级别的缓存,我们看到,在配置了二级缓存以后,当我们session关闭以后,我们再去查询对象的时候,此时hibernate首先会去二级缓存中查询是否有该对象,有就不会再发sql了。

    ②二级缓存缓存的仅仅是对象,如果查询出来的是对象的一些属性,则不会被加到缓存中去

    TestCase2:

    我们看到这个测试用例,如果我们只是取出对象的一些属性的话,则不会将其保存到二级缓存中去,因为二级缓存缓存的仅仅是对象

    ③通过二级缓存来解决 N+1 的问题

    TestCase3:

    当我们如果需要查询出两次对象的时候,可以使用二级缓存来解决N+1的问题。

    ④二级缓存会缓存 hql 语句吗?

    TestCase4:

    Hibernate: select student0_.id as id2_, student0_.name as name2_, 
    student0_.sex as sex2_, student0_.rid as rid2_ from t_student 
    student0_ limit ?
    Hibernate: select student0_.id as id2_, student0_.name as name2_,
     student0_.sex as sex2_, student0_.rid as rid2_ from t_student 
     student0_ limit ?
    我们看到,当我们如果通过 list() 去查询两次对象时,二级缓存虽然会缓存查询出来的对象,但是我们看到发出了两条相同的查询语句,这是因为二级缓存不会缓存我们的hql查询语句,要想解决这个问题,我们就要配置我们的查询缓存了。
    

      

    四、查询缓存(sessionFactory级别)

    我们如果要配置查询缓存,只需要在hibernate.cfg.xml中加入一条配置即可:

    然后我们如果在查询hql语句时要使用查询缓存,就需要在查询语句后面设置这样一个方法:

    如果是在annotation中,我们还需要在这个类上加上这样一个注解:@Cacheable

    接下来我们来通过测试用例来看看我们的查询缓存

    ①查询缓存也是sessionFactory级别的缓存

    TestCase1:

    Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ limit ?
    我们看到,此时如果我们发出两条相同的语句,hibernate也只会发出一条sql,因为已经开启了查询缓存了,并且查询缓存也是sessionFactory级别的
    ②只有当 HQL 查询语句完全相同时,连参数设置都要相同,此时查询缓存才有效
    TestCase2:
    

      

    Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ where student0_.name like ? limit ? 
    Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ where student0_.name like ? limit ?
    
    我们看到,如果我们的hql查询语句不同的话,我们的查询缓存也没有作用
    ③查询缓存也能引起 N+1 的问题
    查询缓存也能引起 N+1 的问题,我们这里首先先将 Student 对象上的二级缓存先注释掉:
    

      

    TestCase4:

    Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ where student0_.name like ? limit ?
    
    Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?
    
    
    Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?
    
    Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?
    
    Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?
    

      

    我们看到,当我们将二级缓存注释掉以后,在使用查询缓存时,也会出现 N+1 的问题,为什么呢?

    因为查询缓存缓存的也仅仅是对象的id,所以第一条 sql 也是将对象的id都查询出来,但是当我们后面如果要得到每个对象的信息的时候,此时又会发sql语句去查询,所以,如果要使用查询缓存,我们一定也要开启我们的二级缓存,这样就不会出现 N+1 问题了

  • 相关阅读:
    Android-Universal-Image-Loader学习笔记(两)--LruDiscCache
    linux 多个源文件在编译时会产生一个目标文件
    springMVC 获取本地项目路径 及后整理上传文件的方法
    Cf 444C DZY Loves Colors(段树)
    什么是EF, 和 Entity Framework Demo简单构建一个良好的发展环境
    Mac下一个/usr/include失踪
    ArcGIS 10 破解安装(win7 64位)
    Android ProgressBar 反向进度条/进度条从右到左走
    Java的位运算符具体解释实例——与(&)、非(~)、或(|)、异或(^)
    poj 3273 Monthly Expense (二分)
  • 原文地址:https://www.cnblogs.com/ipetergo/p/6803290.html
Copyright © 2011-2022 走看看