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"

"<http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd>" >

<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>


原文链接:https://www.cnblogs.com/lone5wolf/p/11065155.html