Hibernate一级缓存、二级缓存以及查询缓存的关系

转载自http://blog.csdn.net/maoyeqiu/article/details/50209893

前两天总结了一下二级缓存和查询缓存的关系,但是又有一个新的问题,就是查询缓存缓存到二级缓存的数据,在第三次(第一次缓存中没有数据,查询数据库将对应的ID值存入到二级缓存中去,第二次如果是同一个Session那么将会把数据一级缓存中的数据返回,如果不是同一个Session而是同一个sessionfactory,那么将会把二级缓存中的数据返回,同时将数据放入到一级缓存中去)获取的时候,不使用查询缓存的方法,而是直接使用一级缓存的方法,能不能将缓存的数据获取到呢,我们现在验证一下。

首先开启一级缓存(默认)、二级缓存和查询缓存

[html] view plain copy

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-configuration PUBLIC
  3. "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  4. "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  5. <hibernate-configuration>
  6. <session-factory>
  7. <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
  8. <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernatedemo</property>
  9. <property name="hibernate.connection.password">root</property>
  10. <property name="hibernate.connection.username">root</property>
  11. <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  12. <property name="show_sql">true</property>
  13. <property name="hbm2ddl.auto">create</property>
  14. <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
  15. <property name="hibernate.cache.use_query_cache">true</property>
  16. <!-- 开启二级缓存,其实hibernate默认就是开启的,这里显示的指定一下 -->
  17. <property name="hibernate.cache.use_second_level_cache">true</property>
  18. <property name="hibernate.generate_statistics">true</property>
  19. <mapping class="hibernate.test.dto.DepartmentEntity"></mapping>
  20. </session-factory>
  21. </hibernate-configuration>

用到的缓存类

[java] view plain copy

  1. package hibernate.test.dto;
  2. @Entity
  3. @Table(name = "DEPARTMENT", uniqueConstraints = {
  4. @UniqueConstraint(columnNames = "ID"),
  5. @UniqueConstraint(columnNames = "NAME") })
  6. @Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="department")
  7. public class DepartmentEntity implements Serializable {
  8. private static final long serialVersionUID = 1L;
  9. @Id
  10. @GeneratedValue(strategy = GenerationType.IDENTITY)
  11. @Column(name = "ID", unique = true, nullable = false)
  12. private Integer id;
  13. @Column(name = "NAME", unique = true, nullable = false, length = 100)
  14. private String name;
  15. public Integer getId() {
  16. return id;
  17. }
  18. public void setId(Integer id) {
  19. this.id = id;
  20. }
  21. public String getName() {
  22. return name;
  23. }
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27. }

启动查询缓存将返回的实体存入到二级缓存中去

[java] view plain copy

  1. //此方法向数据库中存入三条数据
  2. storeData();
  3. Session session = HibernateUtil.getSessionFactory().openSession();
  4. session.beginTransaction();
  5. //开启查询缓存, query.setCacheable(true);开启查询缓存
  6. Query query = session.createQuery("select s from DepartmentEntity s");
  7. query.setCacheable(true);
  8. List<DepartmentEntity> names = query.list();
  9. for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
  10. String name = it.next().getName();
  11. System.out.println(name);
  12. }
  13. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());

执行的结果:

hibernate: select department0_.ID as ID0_, department0_.NAME as NAME0_ from DEPARTMENT department0_
Human Resource
Humanne
Jhon
3

我们可以看到,产生了一条查询语句,将三个实体的名字输出,放入二级缓存的数量是3,都是没有问题的。这里比较容易犯的一个错误是查询的结果并不是实体,而是名字或者是其他属性,比如sql我们这么写:select s.name from DepartmentEntity s。存入二级缓存的数据是0,也就是没有存入到二级缓存中去。

再次调用上述方法:

[java] view plain copy

  1. //开启查询缓存, query.setCacheable(true);开启查询缓存
  2. Query query = session.createQuery("select s from DepartmentEntity s");
  3. query.setCacheable(true);
  4. List<DepartmentEntity> names = query.list();
  5. for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
  6. String name = it.next().getName();
  7. System.out.println(name);
  8. }
  9. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
  10. //同一个Session中再次查询,不会发出SQL语句到数据库
  11. query = session.createQuery("select s from DepartmentEntity s");
  12. query.setCacheable(true);
  13. names = query.list();
  14. for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
  15. String name = it.next().getName();
  16. System.out.println(name);
  17. }
  18. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
  19. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());

执行的结果:

Hibernate: select department0_.ID as ID0_, department0_.NAME as NAME0_ from DEPARTMENT department0_
Human Resource
Humanne
Jhon
3
Human Resource
Humanne
Jhon
3
0

我们可以看到只产生了一条数据库查询语句,第二次直接从缓存中获取(注意,第二次调用的时候依然要写quey.setCacheable(true); ,我们模拟的是同一个方法的多次调用,不然的会产生数据库查询),那么问题来了,这个缓存指的是一级缓存还是二级缓存呢,我们看执行的结果二级缓存的命中是0,也就是说这里并没有用到二级缓存而是直接使用了一级缓存,前提是在同一个Session的情况下,在同一个session下执行的结果都会首先缓存到一级缓存中去,那么我们开一个新的Session会有什么样的不同结果呢

[java] view plain copy

  1. //开启查询缓存, query.setCacheable(true);开启查询缓存
  2. Query query = session.createQuery("select s from DepartmentEntity s");
  3. query.setCacheable(true);
  4. List<DepartmentEntity> names = query.list();
  5. for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
  6. String name = it.next().getName();
  7. System.out.println(name);
  8. }
  9. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
  10. //同一个Session中再次查询,不会发出SQL语句到数据库
  11. query = session.createQuery("select s from DepartmentEntity s");
  12. quey.setCacheable(true);
  13. names = query.list();
  14. for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
  15. String name = it.next().getName();
  16. System.out.println(name);
  17. }
  18. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
  19. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());
  20. Session anotherSession = HibernateUtil.getSessionFactory().openSession();
  21. anotherSession.beginTransaction();
  22. query = anotherSession.createQuery("select s from DepartmentEntity s");
  23. query.setCacheable(true);
  24. names = query.list();
  25. for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
  26. String name = it.next().getName();
  27. System.out.println(name);
  28. }
  29. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
  30. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());

执行结果:

[java] view plain copy

  1. Hibernate: select department0_.ID as ID0_, department0_.NAME as NAME0_ from DEPARTMENT department0_
  2. Human Resource
  3. Humanne
  4. Jhon
  5. 3
  6. Human Resource
  7. Humanne
  8. Jhon
  9. 3
  10. 0
  11. Human Resource
  12. Humanne
  13. Jhon
  14. 3
  15. 3

我们看到运行的结果,三次执行都只是放入二级缓存的实例3个,也就是说二级缓存中只有三个实例。由于二级缓存是sessionfactory级别的当开启查询缓存将数据放入的二级缓存的时候是不受开了几个session影响的,所以尽管我们上边开启了2个session但是依旧是在二级缓存中有3个实体。这里还有一个问题是查询缓存的key值是如何定义的呢,导致了开启了3次查询缓存而只存入3条数据,如果key值不同的话,那么肯定是会存入9条数据,关于这个问题大家可以参考,这里。由于二级缓存是sessionfactory级别的,因此会直接将二级缓存中的数据取出,并存入到一级缓存中去。

我们在sql中只是查询实体的名字,我们来看一下查询缓存是如何缓存的

[java] view plain copy

  1. //开启查询缓存, query.setCacheable(true);开启查询缓存
  2. ,Query query = session.createQuery("select s.name from DepartmentEntity s");
  3. query.setCacheable(true);
  4. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
  5. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());
  6. //同一个Session中再次查询,不会发出SQL语句到数据库
  7. query = session.createQuery("select s.name from DepartmentEntity s");
  8. query.setCacheable(true);
  9. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
  10. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());
  11. Session anotherSession = HibernateUtil.getSessionFactory().openSession();
  12. anotherSession.beginTransaction();
  13. query = anotherSession.createQuery("select s.name from DepartmentEntity s");
  14. query.setCacheable(true);
  15. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
  16. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());

执行结果:

Hibernate: select department0_.NAME as col_0_0_ from DEPARTMENT department0_
0
0
0
0
0
0

从结果上我们可以看到只有一个数据库查询,然后是六个0,前两个0是没有将数据存入到二级缓存中去,中间两个0并且没有sql数据库查询说明,数据从缓存中获取,而且是一级缓存中的数据,后两个0我们重新开启了一个session,同样没有数据库查询,也就说是调用了二级缓存中的数据,也就说明了查询缓存也是sessionfactory级别的。

我们现在来验证一下我们刚才提出的问题,直接用load获取数据

[java] view plain copy

  1. DepartmentEntity department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
  2. System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());

执行结果:

0

从结果来看,没有产生数据库查询二级缓存的命中是0,也就是说数据是从一级缓存中获取的,这就验证了我们一开始提到的答案。

总结:

1、一级缓存是session级别的,二级缓存和查询缓存都是sessionfactory级别的,查询缓存和二级缓存是一起来使用的

2、任何sql执行都会存入到同一个session的一级缓存中去

3、同时开启查询缓存和二级缓存,可以在不同session间共享缓存的结果

4、二级缓存缓存的是实体,不是属性

5、查询缓存的结果如果只是属性,那么查询缓存中存储的是id和属性的值,如果是实体的集合,那么查询缓存存储的只是实体的id,对应的实体会存储到二级缓存中去。

6、不同session间返回数据的顺序是,二级缓存先将数据返回,然后将数据存入本session的一级缓存中去,以便下次调用时的使用

时间: 2024-10-23 03:08:37

Hibernate一级缓存、二级缓存以及查询缓存的关系的相关文章

hibernate一级和二级缓存介绍

一级缓存Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存Hibernate一级缓存又称为"Session的缓存",它是内置的,不能被卸载(不能被卸载的意思就是这种缓存不具有可选性,必须有的功能,不可以取消session缓在第一级缓存中,持久化类的每个实例都具有唯一的OID存 .在缓存中的对象,具有持久性,session对象负责管理.一级缓存的优点是使用同一个session对象多次查询同一个数据对象,仅对数据库查询一次例如:session.load

[原创]java WEB学习笔记93:Hibernate学习之路---Hibernate 缓存介绍,缓存级别,使用二级缓存的情况,二级缓存的架构集合缓存,二级缓存的并发策略,实现步骤,集合缓存,查询缓存,时间戳缓存

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------

hibernate中的二级缓存

二级缓存使用场景:不经常修改的数据,但是经常的访问的数据会放到缓存中去 一级缓存仅仅是session内部的缓存,用来存取sql语句,比如说连续调用两次相同参数的get方法,就是session缓存 二级缓存是sessiionFactory层面的缓存,即不同线程,不同程序之间的缓存 1.在hibernate.hbm.xml中加载配置 <property name="cache.provider_class">org.hibernate.cache.HashtableCacheP

MyBatis Review——查询缓存

一,查询缓存简介 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存. 一级缓存是SqlSession级别的缓存.在操作数据库时候,需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据.不同的sqlSession之间的缓存数据区域是互相不影响的. 二级缓存是mapper级别的缓存,多个sqlSession去操作同一个mapper的sql语句,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSes

30.5. MySQL用户,权限,服务器配置,查询缓存及优化,索引等

MySQL用户和权限管理 元数据数据库:mysql系统授权表:db, host, usercolumns_priv, tables_priv, procs_priv, proxies_priv 用户账号:'USERNAME'@'HOST' @'HOST': 主机名 IP地址或Network可用通配符: % _示例:172.16.%.% 用户管理 创建用户:CREATE USERCREATE USER 'USERNAME'@'HOST' [IDENTIFIED BY 'password']:默认权

详解MySQL查询缓存

查询缓存是指存储使用SELECT语法查询到的返回到客户端的文本.当相同的请求再次发生时,会从查询缓存中获取数据,而非再执行一遍查询.查询缓存是共享Session会话的,所以一个客户端的请求可能与另一个客户端的请求得到相同的结果. 当服务器频繁收到相同的请求而数据库中的表数据变化频率又不高,查询缓存是非常有用的,它可以大大提高应用程序的访问效率.很多Web服务器利用这一原理基于数据库的内容动态生成页面. 查询缓存并不会返回过期的数据,当数据库中的表数据发生变化时,相关的查询缓存会自动清除.但是查询

MySQL查询缓存设置 提高MySQL查询性能

首先看看MSYQL逻辑框架:图片来自高性能mysql 如果使用了QueryCache,当查询接收到一个和之前同样的查询,服务器将会从查询缓存中检索结果,而不是再次分析和执行相同的查询.这样就能大大提高查询性能. 打开查询缓存,要通过几个步骤来设置: 虽然你设置mysql允许查询缓存,但是如果你设置的查询缓存大小为了0,这和没有允许没什么区别. 所以必须是几个步骤的设置才能真正打开查询缓存这个功能. 下面演示最常用的设置查询缓存 一. query_cache_type 使用查询缓存的方式 一般,我

MySQL加速查询速度的独门武器:查询缓存

[导读] 与朋友或同事谈到MySQL查询缓存功能的时候,个人喜欢把查询缓存功能Query Cache比作荔枝, 是非常营养的东西,但是一次性吃太多了,就容易上火而流鼻血,虽然不是特别恰当的比喻,但是有很多相似的地方.另外Query Cache有其特殊的业务场景,MySQL也不像其他数据库产品,缓存查询语句的执行计划等信息,而是直接缓存查询语句的结果集和对应的SQL语句.本文 就给大家介绍下查询缓存的相关知识,希望可以引导大家正确地使用Query Cache独门武器. 对MySQL查询缓存从五个角

《高性能MySQL》读书笔记--查询缓存

1.MySQL查询缓存 很多数据库产品都能够缓存查询的执行计划,对于相同类型的SQL就可以跳过SQL解析和执行计划生成阶段.MySQL还有另一种不同的缓存类型:缓存完整的SELECT查询结果,也就是"查询缓存". 查询缓存系统会跟踪查询中涉及的每个表,如果这些表发生变化,那么和这个表相关的所有的缓存数据都将失效. 查询缓存对应用程序是完全透明的.应用程序无须关心MySQL是通过查询缓存返回的结果还是实际执行返回的结果. 另外,随着现在的通用服务器越来越强大,查询缓存可能是一个影响服务器

MySQL查询缓存设置提高MySQL查询性能

首先看看MSYQL逻辑框架:图片来自高性能mysql 如果使用了QueryCache,当查询接收到一个和之前同样的查询,服务器将会从查询缓存中检索结果,而不是再次分析和执行相同的查询.这样就能大大提高查询性能. 打开查询缓存,要通过几个步骤来设置: 虽然你设置mysql允许查询缓存,但是如果你设置的查询缓存大小为了0,这和没有允许没什么区别. 所以必须是几个步骤的设置才能真正打开查询缓存这个功能. 下面演示最常用的设置查询缓存 一. query_cache_type 使用查询缓存的方式 一般,我