zoukankan      html  css  js  c++  java
  • Java实战之02Hibernate-08二级缓存

    十四、Hibernate的二级缓存

    1Hibernate的缓存结构

    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

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

    注意:

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

    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     }

    8EHCache的配置文件

    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查询只能存不能取,因为语句是动态的。

    解决办法:

    按照语句进行存储。使用MapMap<String,Object>。 keySQL语句。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     }
  • 相关阅读:
    使用微创联合M5S空气检测仪、树莓派3b+、prometheus、grafana实现空气质量持续监控告警WEB可视化
    nodejs:使用puppeteer在服务器中构建一个获取电影电视剧剧集的接口
    nodejs 使用puppeteer模块在nodejs中模拟浏览器运行,载入脚本,输出结果
    nodejs 使用http和fs模块读取网络图片,并写入到本地
    不同环境的性能测试计划
    React 应用的 Nginx 缓存控制
    被【BiliBili@稚晖君】大佬的圈粉-收集下大佬的软硬件工具
    第二章-在线编程题2-求解幸运数问题
    数据库系统概论 第一章绪论知识点 脑图笔记
    第36篇-return字节码指令
  • 原文地址:https://www.cnblogs.com/minihouseCoder/p/5611310.html
Copyright © 2011-2022 走看看