十四、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包加入到构建路径中:

 <!-- 开启hibernate的二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 配置二级缓存的提供商 -->
<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

 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

     <diskStore path="java.io.tmpdir"/>

     <defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>

4、使用二级缓存

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

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

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

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

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

5、类缓存区(Class Cache Region)

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

注意:

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

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

 /*
* 类缓存区
* 都是针对实体类说的,不涉及类中的关联对象。
* 举例:
* 如果配置了客户,只涉及客户的信息,不会涉及客户关联的订单!
* 哪些方法可以操作类缓存区:
* get和load:
* 他们都是可以存和取二级缓存中类缓存区的数据。
* query.list()
* 它只能存,不能取二级缓存的类缓存区数据
*
* 类缓存区,存的是什么?
* 一级缓存:存的是对象
* 二级缓存:存的是散装数据。
* 例如:Cusomert[
* {id:1,name:'testA',age:20},
* {id:2,name:'testB',age:28}
* ]
*/
@Test
public void test2(){
Session s1 = HibernateUtil.getSession();//每次都是获取一个新的Session,不能绑到当前线程上。
Transaction tx1 = s1.beginTransaction();
//使用query的list方法,查询所有客户
Query query1 = s1.createQuery("from Customer");
List list1 = query1.list();//会去查询,同时把查询结果放入一级缓存。如果配置了二级缓存,也会放入二级缓存。
System.out.println(list1);
//使用get方法获取一个客户
Customer c1 = s1.load(Customer.class,1);//不查,因为一级缓存之中一级有了
System.out.println(c1);
tx1.commit();
s1.close();//session一关闭,一级缓存就消失了 Session s2 = HibernateUtil.getSession();
Transaction tx2 = s2.beginTransaction();
//使用get方法获取一个客户
Customer c2 = s2.load(Customer.class,1);//不查,因为二级缓存中有
System.out.println(c2);
Query query2 = s2.createQuery("from Customer");
List list2 = query2.list();//会去查询,不会从二级缓存中取数据
System.out.println(list2);
tx2.commit();
s2.close();
}

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

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

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

7、更新时间戳

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

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

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中配置)

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

b、实验是否可用

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

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. 一些好用的nginx第三方模块

    一些好用的nginx第三方模块 转自;http://macken.iteye.com/blog/1963301  1.Development Kit https://github.com/simpl/ ...

  2. Using breakpad in cocos2d-x 3.2,dump信息收集

    作者:HU 转载请注明,原文链接:http://www.cnblogs.com/xioapingguo/p/4037268.html 一.基本步骤 1.生成转换工具 2.把breakpad加入到项目 ...

  3. UITabBarController详解

    UITabBarController使用详解 UITabBarController是IOS中很常用的一个viewController,例如系统的闹钟程序,ipod程序等.UITabBarControl ...

  4. Mac OS X 10.10优胜美地怎样完美接管iphone上的电话和短信

    自从今年苹果第一次的公布会上毛猫就特别注意这个功能.感觉特别Cool,特别方便.但直到今天毛猫才第一次成功測试出这个功能呀.尽管handoff功能还未測出来,可是认为在mac上发短信和打电话也已经足够 ...

  5. 03---JavaScript基础整理

    一.概述:           Netscape开发的一种基于对象和事件驱动的脚本语言.                  被设计用来想HTML页面添加交互行为.           无需编译,可由浏 ...

  6. Firefly distributed模块的原理与twisted中PB远程调用协议

    这些天断断续续在看Firefly, 看了一下distributed模块的设计,其实就是使用的twisted.spread.pb觉得以后要是想用Firefly有必要了解一下twisted, 所以在网上查 ...

  7. 关于websocket中的心跳..

    客户端的实现:1, 如果你正在对流进行读写,那么表示其实你己经在活跃状态,不需要发送心跳消息2, 如果你的网络是空闲的, 那么需要指定一个时间间隔(如20sec)向server发送心跳消息.所谓的心跳 ...

  8. Android 带进度的圆形进度条

    最近项目有个需求,做带进度从下到上的圆形进度条. 网上查了一下资料,发现这篇博客写得不错http://blog.csdn.net/xiaanming/article/details/10298163 ...

  9. qt helper

    qt帮助文档(中文版) http://www.kuqin.com/qtdocument/index.html qt基础 http://www.devbean.net/2012/08/qt-study- ...

  10. 【大坑】DataGridView多线程更新修改Cell单元格卡死

    最新发现是Column的AutoSizeMode设置为AllCell调整宽度而造成的卡顿,还有就是在现在里面使用Invoke用匿名函数闭包的形式访问For循环的i变量值会不正确导致找不到索引而造成卡顿 ...