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     }
posted @ 2016-06-23 16:39  铭昊Coder  阅读(476)  评论(0编辑  收藏  举报