Hibernate的缓存机制
一、缓存的范围分为3类:
1、事务范围(单Session即一级缓存)
事务范围的缓存只能被当前事务访问,每个事务都有各自的缓存,缓存内的数据通常采用相互关联的对象形式.缓存的生命周期依赖于事务的生命周期,只有当事务结束时,缓存的生命周期才会结束.事务范围的缓存使用内存作为存储介质,一级缓存就属于事务范围.
2、应用范围(单SessionFactory即二级缓存)
应用程序的缓存可以被应用范围内的所有事务共享访问.缓存的生命周期依赖于应用的生命周期,只有当应用结束时,缓存的生命周期才会结束.应用范围的缓存可以使用内存或硬盘作为存储介质,二级缓存就属于应用范围.
3、集群范围(多SessionFactory)
在集群环境中,缓存被一个机器或多个机器的进程共享,缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致,缓存中的数据通常采用对象的松散数据形式.
二、一级缓存:
1、使用一级缓存的目的是为了减少对数据库的访问次数,从而提升hibernate的执行效率;(当执行一次查询操作的时候,执行第二次查询操作,先检查缓存中是否有数据,如果有数据就不查询数据库,直接从缓存中获取数据);
2、Hibernate中的一级缓存,也叫做session的缓存,它可以在session范围内减少数据库的访问次数,只在session范围内有效,session关闭,一级缓存失败;
3、一级缓存的特点,只在session范围有效,作用时间短,效果不是特别明显,在短时间内多次操作数据库,效果比较明显。
4、当调用session的save/saveOrUpdate/get/load/list/iterator方法的时候,都会把对象放入session缓存中;
5、session的缓存是由hibernate维护的,用户不能操作缓存内容;如果想操作缓存内容,必须通过hibernate提供的evict/clear方法操作
6、缓存相关的方法(在什么情况下使用上面方法呢?批量操作情况下使用,如Session.flush();先与数据库同步,Session.clear();再清空一级缓存内容):
session.flush();让一级缓存与数据库同步;
session.evict();清空一级缓存中指定的对象;
session.clear();清空一级缓存中所有的对象;
二、二级缓存 : 基于应用程序级别的缓存,作用在SessionFactory范围内的, 可以跨多个session,即不同的session都可以访问缓存。 Hibernate提供的二级缓存有默认的实现,且是一种可插配的缓存框架!如果用户想用二级缓存,只需要在hibernate.cfg.xml中配置即可; 不想用,直接移除,不影响代码。
1、在执行各种条件查询时,如果所获得的结果集为实体对象的集合,那么就会把所有的数据对象根据ID放入到二级缓存中。
2、当Hibernate根据ID访问数据对象的时候,首先会从Session一级缓存中查找,如果查不到并且配置了二级缓存,那么会从二级缓存中查找,如果还查不到,就会查询数据库,把结果按照ID放入到缓存中。
3、删除、更新、增加数据的时候,同时更新缓存。
注意:
在通常情况下会将具有以下特征的数据放入到二级缓存中:
● 很少被修改的数据。
● 不是很重要的数据,允许出现偶尔并发的数据。
● 不会被并发访问的数据。
● 常量数据。
● 不会被第三方修改的数据
而对于具有以下特征的数据则不适合放在二级缓存中:
● 经常被修改的数据。
● 财务数据,绝对不允许出现并发。
● 与其他应用共享的数据。
三、二级缓存配置步骤: 在默认情况下,Hibernate会使用EHCache作为二级缓存组件。但是,可以通过设置hibernate.cache.provider_class属性,指定其他的缓存策略,该缓存策略必须实现org.hibernate.cache.CacheProvider接口。 通过实现org.hibernate.cache.CacheProvider接口可以提供对不同二级缓存组件的支持,此接口充当缓存插件与Hibernate之间的适配器。
组件
|
Provider类
|
类型
|
集群
|
查询缓存
|
Hashtable
|
org.hibernate.cache.HashtableCacheProvider
|
内存
|
不支持
|
支持
|
EHCache
|
org.hibernate.cache.EhCacheProvider
|
内存,硬盘
|
不支持
|
支持
|
OSCache
|
org.hibernate.cache.OSCacheProvider
|
内存,硬盘
|
支持
|
支持
|
SwarmCache
|
org.hibernate.cache.SwarmCacheProvider
|
集群
|
支持
|
不支持
|
JBoss TreeCache
|
org.hibernate.cache.TreeCacheProvider
|
集群
|
支持
|
支持
|
1.添加jar包,如ehcache-core.jar
2.在hibernate.cfg.xml开启二级缓存Xml代码
<!-- a. 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- b. 指定使用哪一个缓存框架(默认提供的) -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
<!-- 开启查询缓存 -->
<property name="hibernate.cache.use_query_cache">true</property>
<!-- c. 指定哪一些类,需要加入二级缓存 -->
<class-cache usage="read-write" class="com.bie.lesson11.Dept"/>
<class-cache usage="read-only" class="com.bie.lesson11.Employee"/>
<!-- 集合缓存[集合缓存的元素对象,也加加入二级缓存] -->
<collection-cache usage="read-write" collection="com.bie.lesson11.Dept.emps"/>
3.ehcache.xml配置(可选)
cache参数详解:
● name:指定区域名
● maxElementsInMemory :缓存在内存中的最大数目
● maxElementsOnDisk:缓存在磁盘上的最大数目
● eternal :设置是否永远不过期
● overflowToDisk : 硬盘溢出数目
● timeToIdleSeconds :对象处于空闲状态的最多秒数后销毁
● timeToLiveSeconds :对象处于缓存状态的最多秒数后销毁
● memoryStoreEvictionPolicy:缓存算法,有LRU(默认)、LFU、LFU
关于缓存算法,常见有三种:
● LRU:(Least Rencently Used)新来的对象替换掉使用时间算最近很少使用的对象
● LFU:(Least Frequently Used)替换掉按命中率高低算比较低的对象
● LFU:(First In First Out)把最早进入二级缓存的对象替换掉
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--如果缓存中的对象存储超过指定的缓存数量的对象存储的磁盘地址-->
<diskStore path="D:/ehcache"/>
<!-- 默认cache:如果没有对应的特定区域的缓存,就使用默认缓存 -->
<defaultCache maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="false"/>
<!-- 指定区域cache:通过name指定,name对应到Hibernate中的区域名即可-->
<cache name="cn.javass.h3test.model.UserModel"
eternal="false"
maxElementsInMemory="100"
timeToIdleSeconds="1200"
timeToLiveSeconds="1200"
overflowToDisk="false">
</cache>
</ehcache>
4.实体类的映射文件配置,如user.hbm.xml的配置
<?xml version="1.0" encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
<hibernate-mapping>
<class>
<!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write nonstrict-read-write transactional-->
<class name="cn.java.test.model.User" table="TBL_USER">
<cache usage="read-write"/>
......
</class>
</hibernate-mapping>
(1)其中二级缓存并发访问策略有三种:
a.在user.hbm.xml中添加<cache usage="read-write"/>
b.在hibernate.cfg.xml中添加<class-cache usage="read-write" class="com.hb.test.User"/>
c.用Hibernate注解配置缓存实体类
(2) 二级缓存策略
● READ_ONLY:实体只读缓存
只读缓存不允许更新,将报错Can't write to a readonly object。
允许新增,(从2.0以后新增直接添加到二级缓存)
//确保数据库中有标识符为1的FarmModel
FarmModel farm = (FarmModel) session.get(FarmModel.class, 1);
//如果修改将报错,只读缓存不允许修改
//farm.setName("aaa");
● NONSTRICT_READ_WRITE:实体非严格读/写缓存
允许更新,更新后缓存失效,需再查询一次。
允许新增,新增记录自动加到二级缓存中。
整个过程不加锁。
● READ_WRITE:实体读/写缓存
允许更新,更新后自动同步到缓存。
允许新增,新增记录后自动同步到缓存。
保证read committed隔离级别及可重复读隔离级别(通过时间戳实现)
整个过程加锁,如果当前事务的时间戳早于二级缓存中的条目的时间戳,说明该条目已经被别的
事务修改了,此时重新查询一次数据库,否则才使用缓存数据,因此保证可重复读隔离级别。
读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁
● TRANSACTIONAL:实体事务缓存
缓存支持事务,发生异常的时候,缓存也能够回滚,只支持jta环境
● Collection集合缓存
<hibernate-mapping>
<class name="cn.java.test.model.UserModel" table="TBL_USER">
<cache usage="read-write" />
<set name="farms" cascade="all" inverse="true" lazy="false">
<cache usage="read-write"/>
<key column="fk_user_id"/>
<one-to-many class="cn.java.test.model.FarmModel"/>
</set>
</class>
</hibernate-mapping>