十四、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. 支持Git的代码托管网站

    支持Git的代码托管网站: https://github.com/https://code.google.com http://www.codeplex.com/ http://git.oschina ...

  2. Asp.net中使用资源文件实现网站多语言

    首先需要新建一个ASP.NET Web Application.然后右键项目文件Add->Add ASP.NET Folder->App-GlobalResources. 新建好资源文件夹 ...

  3. 【转】简明vim练级攻略

    本文来自:http://coolshell.cn/articles/5426.html vim的学习曲线相当的大(参看各种文本编辑器的学习曲线),所以,如果你一开始看到的是一大堆VIM的命令分类,你一 ...

  4. Android下pm命令详解

    在看相关PackageManager代码时,无意中发现Android 下提供一个pm命令,通常放在/system/bin/下.这个命令与Package有关,且非常实用.所以研究之. 0. Usage: ...

  5. yum、RPM常用的命令(转)

    # yum install xxx            安装xxx软件# yum info xxx                查看xxx软件的信息# yum remove xxx         ...

  6. AIR 移动设备上的存储控制

    File.documentsDirectory, File.userDirectory, File.desktopDirectory 等.可以保存大的数据,如图片,视屏,和临时文件.访问这些文件的全选 ...

  7. Simulate android behaviors on win32

    To make debugging android games on win32 more convenience, we added some simulate actions to win32 p ...

  8. mysql语句在客户端与服务端的基本使用

    //把数据库导出到脚本文件mysqldump -uroot -p1234 --databases abc > d:/a/abc.sql------------------------------ ...

  9. Android打包程序

    右击项目->导出export next,完成相关信息填写将得到.apk文件,即可部署到手机上. 第一次: 然后打开目录就可以看到生成的apk,可以发布到各大市场上.

  10. linux下修改环境变量

    把/etc/apache/bin目录添加到PATH中,方法有三: 1.#PATH=$PATH:/etc/apache/bin 使用这种方法,只对当前会话有效,也就是说每当登出或注销系统以后,PATH ...