十四、Hibernate的二级缓存

1、Hibernate的缓存结构

2、由于二级缓存被多线程共享,就必须有一定的事务访问策略

非严格读写:READ UNCOMMITTED

读写型:READ COMMITTED

事务型:REPEATABLED READ

只读型:SERIALIZABLE

适合放入二级缓存中的数据:

很少被修改

不是很重要的数据, 允许出现偶尔的并发问题

不适合放入二级缓存中的数据:

经常被修改

财务数据, 绝对不允许出现并发问题

与其他应用数据共享的数据

3、缓存提供的供应商

3.1、各个提供商介绍

Hibernate 的二级缓存是进程或集群范围内的缓存, 缓存中存放的是对象的散装数据,二级缓存是可配置的的插件, Hibernate 允许选用以下类型的缓存插件:

a) EHCache: 可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 对 Hibernate 的查询缓存提供了支持

b) OpenSymphony `:可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 提供了丰富的缓存数据过期策略, 对 Hibernate 的查询缓存提供了支持

c) SwarmCache: 可作为集群范围内的缓存, 但不支持 Hibernate 的查询缓存

d) JBossCache:可作为集群范围内的缓存, 支持 Hibernate 的查询缓存

3.2、采用EHCache第三方组件

3.2.1、把所需jar包加入到构建路径中:

  1. <!-- 开启hibernate的二级缓存 -->
  2. <property name="hibernate.cache.use_second_level_cache">true</property>
  3. <!-- 配置二级缓存的提供商 -->
  4. <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>

3.2.3、在应用中加入EHCache的配置文件

把ehcache-1.5.0.jar包打开,把ehcache-failsafe.xml拷贝出来,去掉里面的注释。并把文件名改为ehcache.xml

  1. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
  2.  
  3. <diskStore path="java.io.tmpdir"/>
  4.  
  5. <defaultCache
  6. maxElementsInMemory="10000"
  7. eternal="false"
  8. timeToIdleSeconds="120"
  9. timeToLiveSeconds="120"
  10. overflowToDisk="true"
  11. maxElementsOnDisk="10000000"
  12. diskPersistent="false"
  13. diskExpiryThreadIntervalSeconds="120"
  14. memoryStoreEvictionPolicy="LRU"
  15. />
  16. </ehcache>

4、使用二级缓存

4.1、配置使用二级缓存实体

  1. <!-- 配置哪些类使用二级缓存 -->
  2. <class-cache usage="read-write" class="cn.itcast.domain.Customer"/>
  3. <class-cache usage="read-write" class="cn.itcast.domain.Order"/>
  4. <!-- 配置哪些集合使用二级缓存 -->
  5. <collection-cache usage="read-write" collection="cn.itcast.domain.Customer.orders"/>

4.2、验证二级缓存是有效的

注意:为了验证二级缓存的存在及有效,先不要使用把session绑定到当前线程上。同时也不要使用getCurrentSession()方法。

  1. //验证二级缓存确实可用
  2. @Test
  3. public void test1(){
  4. Session s1 = HibernateUtil.getSession();//每次都是获取一个新的Session,不能绑到当前线程上。
  5. Transaction tx1 = s1.beginTransaction();
  6. //使用get方法获取一个客户
  7. Customer c1 = s1.get(Customer.class,1);//会去数据库中查询,把结果放入一级缓存之中。 如果配置了二级缓存,还会把查询结果放入二级缓存之中。
  8. System.out.println(c1);
  9. tx1.commit();
  10. s1.close();//session一关闭,一级缓存就消失了
  11.  
  12. Session s2 = HibernateUtil.getSession();//每次都是获取一个新的Session,不能绑到当前线程上。
  13. Transaction tx2 = s2.beginTransaction();
  14. //使用get方法获取一个客户
  15. Customer c2 = s2.get(Customer.class,1);//不查。二级缓存中有
  16. System.out.println(c2);
  17. tx2.commit();
  18. s2.close();
  19. }

5、类缓存区(Class Cache Region)

明确存的是什么:是对象中的数据,而不是一个对象。

注意:

get和load都可以存和取二级缓存的数据。

Query.list("from Customer")只能存不能取。原因:动态查询。HQL是不一定的。

  1. /*
  2. * 类缓存区
  3. * 都是针对实体类说的,不涉及类中的关联对象。
  4. * 举例:
  5. * 如果配置了客户,只涉及客户的信息,不会涉及客户关联的订单!
  6. * 哪些方法可以操作类缓存区:
  7. * get和load:
  8. * 他们都是可以存和取二级缓存中类缓存区的数据。
  9. * query.list()
  10. * 它只能存,不能取二级缓存的类缓存区数据
  11. *
  12. * 类缓存区,存的是什么?
  13. * 一级缓存:存的是对象
  14. * 二级缓存:存的是散装数据。
  15. * 例如:Cusomert[
  16. * {id:1,name:'testA',age:20},
  17. * {id:2,name:'testB',age:28}
  18. * ]
  19. */
  20. @Test
  21. public void test2(){
  22. Session s1 = HibernateUtil.getSession();//每次都是获取一个新的Session,不能绑到当前线程上。
  23. Transaction tx1 = s1.beginTransaction();
  24. //使用query的list方法,查询所有客户
  25. Query query1 = s1.createQuery("from Customer");
  26. List list1 = query1.list();//会去查询,同时把查询结果放入一级缓存。如果配置了二级缓存,也会放入二级缓存。
  27. System.out.println(list1);
  28. //使用get方法获取一个客户
  29. Customer c1 = s1.load(Customer.class,1);//不查,因为一级缓存之中一级有了
  30. System.out.println(c1);
  31. tx1.commit();
  32. s1.close();//session一关闭,一级缓存就消失了
  33.  
  34. Session s2 = HibernateUtil.getSession();
  35. Transaction tx2 = s2.beginTransaction();
  36. //使用get方法获取一个客户
  37. Customer c2 = s2.load(Customer.class,1);//不查,因为二级缓存中有
  38. System.out.println(c2);
  39. Query query2 = s2.createQuery("from Customer");
  40. List list2 = query2.list();//会去查询,不会从二级缓存中取数据
  41. System.out.println(list2);
  42. tx2.commit();
  43. s2.close();
  44. }

6、集合缓存区(Collection Cache Region)

一对多关系映射:操作多的一方就是集合。在配置集合映射时,需注意:

  1. /*
  2. * 集合缓存区
  3. * 要想使用集合缓存区:
  4. * 1、必须在主配置文件中配置开启集合缓存区。
  5. * 2、必须同时配置上集合元素的类缓存区。
  6. * 要想使用orders集合缓存区,以下两行缺一不可
  7. * <class-cache usage="read-write" class="cn.itcast.domain.Order"/>
  8. <collection-cache usage="read-write" collection="cn.itcast.domain.Customer.orders"/>
  9. 集合缓存区,存入的是什么?
  10. 是只有OID的一个集合。
  11. 举例:
  12. {id:1,id:2,id:3,id:4....}
  13. 执行方式:
  14. 在使用集合缓存区时,会先从集合缓存区去匹配OID,把匹配上的OID全部取出,到对应的类缓存区去取数据,再生成对象
  15. */
  16. @Test
  17. public void test3(){
  18. Session s1 = HibernateUtil.getSession();//每次都是获取一个新的Session,不能绑到当前线程上。
  19. Transaction tx1 = s1.beginTransaction();
  20. //使用get方法获取一个客户
  21. Customer c1 = s1.load(Customer.class,1);
  22. //输出客户的订单
  23. System.out.println(c1.getOrders());//由于有延迟加载的存在,此时才会去查询。并且把查询结果存入一级缓存之中。同时也会存入二级缓存。
  24. tx1.commit();
  25. s1.close();//session一关闭,一级缓存就消失了
  26.  
  27. Session s2 = HibernateUtil.getSession();
  28. Transaction tx2 = s2.beginTransaction();
  29. //使用get方法获取一个客户
  30. Customer c2 = s2.load(Customer.class,1);
  31. //获取该客户的订单
  32. System.out.println(c2.getOrders());
  33. tx2.commit();
  34. s2.close();
  35. }

7、更新时间戳

当我们修改一级缓存的数据时,会自动同步二级缓存的数据。用的是时间戳原理。

  1. /*
  2. * 更新时间戳
  3. *
  4. * 时间戳原理:
  5. * 当一级缓存和二级缓存在创建时,都会有两个时间点。
  6. * 其一:创建时间
  7. * 其二:最后修改时间
  8. * 当执行update后,由于一级缓存已经发生变化了,这时hibernate会用一级缓存的最后修改时就
  9. * 和二级缓存的最后修改时间进行比较,用离当前时间近的去修改离当前时间远的。
  10. */
  11. @Test
  12. public void test4(){
  13. Session s1 = HibernateUtil.getSession();//每次都是获取一个新的Session,不能绑到当前线程上。
  14. Transaction tx1 = s1.beginTransaction();
  15. //使用get方法获取一个客户
  16. Customer c1 = s1.get(Customer.class,1);//会查询,同时存入一级缓存和二级缓存
  17. c1.setName("泰斯特");
  18. tx1.commit();//由于快照机制,此行会执行更新,同时更新一级缓存。也会更新二级缓存。
  19. s1.close();//session一关闭,一级缓存就消失了
  20.  
  21. Session s2 = HibernateUtil.getSession();//每次都是获取一个新的Session,不能绑到当前线程上。
  22. Transaction tx2 = s2.beginTransaction();
  23. //使用get方法获取一个客户
  24. Customer c2 = s2.get(Customer.class,1);//不查。二级缓存中有
  25. System.out.println(c2);//输出的【泰斯特】还是【testA】?
  26. tx2.commit();
  27. s2.close();
  28. }

8、EHCache的配置文件

diskStore :指定数据存储位置,可指定磁盘中的文件夹位置
defaultCache : 默认的管理策略

以下属性是必须的:
name: Cache的名称,必须是唯一的(ehcache会把这个cache放到HashMap里)。
maxElementsInMemory: 在内存中缓存的element的最大数目。 
maxElementsOnDisk: 在磁盘上缓存的element的最大数目,默认值为0,表示不限制。 
eternal: 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。 
overflowToDisk: 如果内存中数据超过内存限制,是否要缓存到磁盘上。
以下属性是可选的: 
timeToIdleSeconds: 对象空闲时间,指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问。
timeToLiveSeconds: 对象存活时间,指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问。
diskPersistent: 是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false。 
diskExpiryThreadIntervalSeconds: 对象检测线程运行时间间隔。标识对象状态的线程多长时间运行一次。 
diskSpoolBufferSizeMB: DiskStore使用的磁盘大小,默认值30MB。每个cache使用各自的DiskStore。 
memoryStoreEvictionPolicy:
 如果内存中数据超过内存限制,向磁盘缓存时的策略。默认值LRU,可选FIFO、LFU。
缓存的3 种清空策略 :
FIFO ,first in first out (先进先出).
LFU , Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。
LRU ,Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

9、查询缓存区(Query cache)

问题:

Query查询只能存不能取,因为语句是动态的。

解决办法:

按照语句进行存储。使用Map。Map<String,Object>。 key是SQL语句。value是查询的结果集。

这个Map就是查询缓存区。它默认是关闭的。

注意事项:

放到查询缓冲区中的数据,一定要不怎么变化的数据。(并且是非敏感数据,其实放到任何缓存中的数据都应该是非敏感数据)

使用:

a、开启查询缓存区(在hibernate.cfg.xml中配置)

  1. <!-- 开启hibernate的查询缓存区:有些地方(书籍或者公司)可能会把查询缓存区叫成hibernate的三级缓存 -->
  2. <property name="hibernate.cache.use_query_cache">true</property>

b、实验是否可用

  1. /*
  2. * 为什么query的list方法对二级缓存是能存不能取?
  3. * 原因:
  4. * 因为HQL语句是不定的,hibernate没法确定每次查询的HQL语句都是一样。
  5. *
  6. * 解决不能取的思路:
  7. * Map<String hql,Object result> queryCache;
  8. * 思路:
  9. * 创建一个新的区域,区域可能是一个map。
  10. * map的key是查询的HQL语句。
  11. * map的value是查询的结果集。
  12. *
  13. * hibernate的查询缓存区:
  14. * 1、即是开启了二级缓存,hibernate也不会开启查询缓存区。查询缓存区必须独立开启,且必须是在二级缓存已经开启的基础之上。
  15. * <!-- 开启hibernate的查询缓存区:有些地方(书籍或者公司)可能会把查询缓存区叫成hibernate的三级缓存 -->
  16. <property name="hibernate.cache.use_query_cache">true</property>
  17. 2、在执行查询的时候,需要设置使用查询缓存区
  18. query1.setCacheable(true);//明确使用查询缓存区
  19. *
  20. */
  21. @Test
  22. public void test5(){
  23. Session s1 = HibernateUtil.getSession();//每次都是获取一个新的Session,不能绑到当前线程上。
  24. Transaction tx1 = s1.beginTransaction();
  25. Query query1 = s1.createQuery("from Customer");
  26. query1.setCacheable(true);//明确使用查询缓存区
  27. List list1 = query1.list();
  28. System.out.println(list1);
  29. tx1.commit();
  30. s1.close();//session一关闭,一级缓存就消失了
  31.  
  32. Session s2 = HibernateUtil.getSession();
  33. Transaction tx2 = s2.beginTransaction();
  34. Query query2 = s2.createQuery("from Customer");
  35. query2.setCacheable(true);
  36. List list2 = query2.list();
  37. System.out.println(list2);
  38. tx2.commit();
  39. s2.close();
  40. }

Java实战之02Hibernate-08二级缓存的更多相关文章

  1. JavaEE Tutorials (15) - 对Java持久化API应用使用二级缓存

    15.1二级缓存概述190 15.1.1控制实体是否可以缓存19115.2指定缓存模式设置来提高性能192 15.2.1设置缓存获取和存储模式192 15.2.2通过编程方式控制二级缓存194

  2. 「小程序JAVA实战」小程序数据缓存API(54)

    转自:https://idig8.com/2018/09/22/xiaochengxujavashizhanxiaochengxushujuhuancunapi52/ 刚开始写小程序的时候,用户信息我 ...

  3. TZ_02MyBatis_一级缓存和二级缓存

    1.Mybatis中的缓存 1>什么是缓存        存在于内存中的临时数据.   2> 为什么使用缓存        减少和数据库的交互次数,提高执行效率.   3>什么样的数 ...

  4. mybatis结合redis实战二级缓存(六)

    之前的文章中我们意见分析了一级缓存.二级缓存的相关源码和基本原理,今天我们来分享下了mybatis二级缓存和redis的结合,当然mybatis二级缓存也可以和ehcache.memcache.OSC ...

  5. mybatis结合redis实战二级缓存

    之前的文章中我们意见分析了一级缓存.二级缓存的相关源码和基本原理,今天我们来分享下了mybatis二级缓存和redis的结合,当然mybatis二级缓存也可以和ehcache.memcache.OSC ...

  6. 【Java EE 学习 48】【Hibernate学习第五天】【抓取策略】【二级缓存】【HQL】

    一.抓取策略. 1.hibernate中提供了三种抓取策略. (1)连接抓取(Join Fetch):这种抓取方式是默认的抓取方式.使用这种抓取方式hibernate会在select中内连接的方式获取 ...

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

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

  8. Java面试题:Hibernate的二级缓存与Hibernate多表查询

    我们来看两个有关Java框架之Hibernate的面试题,这是关于Hibernate的常考知识点. 1.请介绍一下Hibernate的二级缓存 解题按照以下思路来回答: (1)首先说清楚什么是缓存: ...

  9. 【.NET Core项目实战-统一认证平台】第十五章 网关篇-使用二级缓存提升性能

    [.NET Core项目实战-统一认证平台]开篇及目录索引 一.背景 首先说声抱歉,可能是因为假期综合症(其实就是因为懒哈)的原因,已经很长时间没更新博客了,现在也调整的差不多了,准备还是以每周1-2 ...

随机推荐

  1. Java条件语句 switch case

    不得不说的几点小秘密: 1. switch 后面小括号中表达式的值必须是整型或字符型 2. case 后面的值可以是常量数值,如 1.2:也可以是一个常量表达式,如 2+2 :但不能是变量或带有变量的 ...

  2. 使用ApplicationLoader中出现报错:The IPA is invalid. It does not inlude a Payload directory

    问题处理方法: 1.将achieve的.app后缀的软件包放在一个payload的文件夹中 2.压缩该文件夹,改变.zip后缀为.ipa 3.使用applicationLoader上传该文件  

  3. 【转】C++对象内存分配问题

    原文:http://blog.csdn.net/c504665913/article/details/7797859 如果一个人自称为程序高手,却对内存一无所知,那么我可以告诉你,他一定在吹牛.用C或 ...

  4. ThinkPHP CURD方法盘点:where方法

    今天来给大家讲下查询最常用但也是最复杂的where方法,where方法也属于模型类的连贯操作方法之一,主要用于查询和操作条件的设置.where方法的用法是ThinkPHP查询语言的精髓,也是Think ...

  5. Codeforces Round #323 (Div. 1) B. Once Again... 暴力

    B. Once Again... Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/582/probl ...

  6. android内核读取file文件

    内核读取file文件的方法: char* file_read(const char * file_path) { struct file *file = NULL; //保存打开文件的文件指针变量 s ...

  7. WIX在VS2012中如何制作中文安装包

    WIX安装图文并茂简易说明一文中介绍了WIX安装包的制作过程,不过生成的是英文版的,如果需要制作中文版的安装包呢? 方法很简单,只需要两步. 1.增加中文UI的文件WixUI_zh-cn.wxl到工程 ...

  8. 打开已存在 Android项目及常见的问题

    Eclipse 打开已存在 Android项目及常见的问题   1.  点击菜单“File”-- "Import",会弹出 Import 对话框:   2,  选择“General ...

  9. Folder and jar

  10. Using zend-navigation in your Album Module

    Using zend-navigation in your Album Module In this tutorial we will use the zend-navigation componen ...