hibernate的查询缓存和二级缓存的配合使用

我的上一篇博客Hibernate缓存体系之查询缓存(query cache),以及list和iterate方法的区别介绍了查询缓存的概念,以及list和iterate的区别。读者可能注意到:那篇博客测试的前提是,只开启查询缓存,没有开启二级缓存。

       通过各种情形测试,我们可以得出一个结论:只开启查询缓存,不开启二级缓存是没有意义的。为什么这么说呢?上一篇博客可以看出,不开启二级缓存,iterate()方法存在N+1次数据库查询问题,list方法最多只能命中id,也需要N次数据库查询,无论是哪儿种情况,查询缓存已经失去了意义。这篇博客我们开启二级缓存的情况下,将上篇博客的情况再测试下,看下会是什么效果。hibernate4.1.6在hibernate.cfg.xml进行如下配置,可以开启二级缓存。

<property name="cache.use_second_level_cache">true</property>
<property name="cache.region.factory_class">org.hibernate.cache.EhCacheRegionFactory</property>


由于测试代码和数据库都和上篇博客一模一样,唯一不同就是开启了二级缓存。这里不再附代码,直接通过测试结果来分析结论。


1. 2次list()执行结果分析

     testUseList()执行结果如下:

Hibernate: 
    select
        student0_.id as id0_,
        student0_.name as name0_,
        student0_.age as age0_ 
    from
        Student student0_ 
    where
        student0_.name='zhangsan111'
list语句测试query cache:hibernate.Student@d6c07[id=1, name=zhangsan111, age=18]
list语句测试query cache:hibernate.Student@6279d[id=2, name=zhangsan111, age=18]
list语句测试query cache:hibernate.Student@12dd76[id=3, name=zhangsan111, age=18]
-------list进行第二次查询------
list语句测试query cache:hibernate.Student@1e6696c[id=1, name=zhangsan111, age=18]
list语句测试query cache:hibernate.Student@135133[id=2, name=zhangsan111, age=18]
list语句测试query cache:hibernate.Student@381d92[id=3, name=zhangsan111, age=18]
可以发现list()第一次发出1条sql语句,第二次查询没有访问数据库,直接从缓存中获取的数据。在开启查询缓存和二级缓存的情况下,list将获取到的id放入查询缓存,key是sql语句;将实体对象放入二级缓存,key是实体对象的主键值。list会首先根据sql语句去查询缓存中获取id,如果没有获取到则发出1条sql查询语句,查询出所有需要的字段值。


2. 2次iterate()执行结果分析

     testUseIterator()执行结果如下:

<span style="font-size:14px;">Hibernate: 
    select
        student0_.id as col_0_0_ 
    from
        Student student0_ 
    where
        student0_.name='zhangsan111'
Hibernate: 
    select
        student0_.id as id0_0_,
        student0_.name as name0_0_,
        student0_.age as age0_0_ 
    from
        Student student0_ 
    where
        student0_.id=?
iterate语句测试query cache:hibernate.Student@1f7cdc7[id=1, name=zhangsan111, age=18]
Hibernate: 
    select
        student0_.id as id0_0_,
        student0_.name as name0_0_,
        student0_.age as age0_0_ 
    from
        Student student0_ 
    where
        student0_.id=?
iterate语句测试query cache:hibernate.Student@135133[id=2, name=zhangsan111, age=18]
Hibernate: 
    select
        student0_.id as id0_0_,
        student0_.name as name0_0_,
        student0_.age as age0_0_ 
    from
        Student student0_ 
    where
        student0_.id=?
iterate语句测试query cache:hibernate.Student@1042fcc[id=3, name=zhangsan111, age=18]
-------iterate进行第二次查询------
Hibernate: 
    select
        student0_.id as col_0_0_ 
    from
        Student student0_ 
    where
        student0_.name='zhangsan111'
iterate语句测试query cache:hibernate.Student@ad339b[id=1, name=zhangsan111, age=18]
iterate语句测试query cache:hibernate.Student@14c4d61[id=2, name=zhangsan111, age=18]
iterate语句测试query cache:hibernate.Student@6c5356[id=3, name=zhangsan111, age=18]</span>
第一次iterate()查询,由于查询缓存和二级缓存中都没有数据,需要进行N+1次数据库查询;第二次查询,只发出一条sql查询主键值,后续根据主键值从二级缓存中获取详细信息。iterate方法会将获取到的实体对象存入二级缓存,至于是否会将id存入查询缓存,这里还不能确定,不过可以肯定的是,iterate不会利用查询缓存中的id值,而是每次查询都重新使用sql查询满足条件的记录主键值。之后再根据主键从二级缓存中查询实体对象。


3. 先iterate后list执行结果分析

     testIteratorAndList1()执行结果如下:

<span style="font-size:14px;">Hibernate: 
    select
        student0_.id as col_0_0_ 
    from
        Student student0_ 
    where
        student0_.name='zhangsan111'
Hibernate: 
    select
        student0_.id as id0_0_,
        student0_.name as name0_0_,
        student0_.age as age0_0_ 
    from
        Student student0_ 
    where
        student0_.id=?
iterate语句测试query cache:hibernate.Student@58cca9[id=1, name=zhangsan111, age=18]
Hibernate: 
    select
        student0_.id as id0_0_,
        student0_.name as name0_0_,
        student0_.age as age0_0_ 
    from
        Student student0_ 
    where
        student0_.id=?
iterate语句测试query cache:hibernate.Student@1042fcc[id=2, name=zhangsan111, age=18]
Hibernate: 
    select
        student0_.id as id0_0_,
        student0_.name as name0_0_,
        student0_.age as age0_0_ 
    from
        Student student0_ 
    where
        student0_.id=?
iterate语句测试query cache:hibernate.Student@38d460[id=3, name=zhangsan111, age=18]
-------第一次使用iterate,第二次使用list查询------
Hibernate: 
    select
        student0_.id as id0_,
        student0_.name as name0_,
        student0_.age as age0_ 
    from
        Student student0_ 
    where
        student0_.name='zhangsan111'
list语句测试query cache:hibernate.Student@7efa96[id=1, name=zhangsan111, age=18]
list语句测试query cache:hibernate.Student@75c78d[id=2, name=zhangsan111, age=18]
list语句测试query cache:hibernate.Student@3acc67[id=3, name=zhangsan111, age=18]</span>
这里可以看出,iterate的缓存信息对于list是没有帮助的。list会发出1条sql,从数据库中查询所有需要的字段值。这里可以看出:iterate方法不会将主键值放到查询缓存中,因为如果放入查询缓存的话,这里的list是没有必要再发出sql查询的。


4. 先listiterate执行结果分析

     testIteratorAndList2()执行结果如下:

<span style="font-size:14px;">Hibernate: 
    select
        student0_.id as id0_,
        student0_.name as name0_,
        student0_.age as age0_ 
    from
        Student student0_ 
    where
        student0_.name='zhangsan111'
list语句测试query cache:hibernate.Student@67c1a6[id=1, name=zhangsan111, age=18]
list语句测试query cache:hibernate.Student@12f41a5[id=2, name=zhangsan111, age=18]
list语句测试query cache:hibernate.Student@17ebe66[id=3, name=zhangsan111, age=18]
-------第一次使用list,第二次使用iterate查询------
Hibernate: 
    select
        student0_.id as col_0_0_ 
    from
        Student student0_ 
    where
        student0_.name='zhangsan111'
iterate语句测试query cache:hibernate.Student@feecca[id=1, name=zhangsan111, age=18]
iterate语句测试query cache:hibernate.Student@6bd9e0[id=2, name=zhangsan111, age=18]
iterate语句测试query cache:hibernate.Student@11710be[id=3, name=zhangsan111, age=18]</span>
这里能够得出的结论,跟2次list()查询差不多。唯一的差别是:iterate每次查询都会发出查询实体对象id的sql语句。


5. 总结

通过开启查询缓存和二级缓存,同样的sql查询可以直接使用查询缓存中的id和二级缓存中的实体对象,能够有效的减少重复的数据库查询,能够提高查询效率。也就是说:同时开启查询缓存和二级缓存是有意义的,也是实际使用hibernate的最佳配置。

进一步的,我们也可以看出list和iterate方法的区别。list()会将实体对象的id放入查询缓存,将实体对象本身放入二级缓存;iterate不会将实体对象的id放入查询缓存,但是会将实体对象本身存入二级缓存。

如果第二次查询能够命中的情况下:list完全不需要查询数据库,可以先从查询缓存中获取到id,再从二级缓存中获取实体对象;iterate一定会发出一条查id的sql,然后去二级缓存中获取实体对象。

至此hibernate的二级缓存、查询缓存的关系已经讲完,list和iterate区别也已经测试出。希望能对大家有帮助,如果错误,欢迎大牛们指正。


你可能感兴趣的:(查询缓存和二级缓存,list和iterate的区别,正确使用二级缓存和查询缓存)