关于hibernate的缓存

[b]Hibernate缓存的概念[/b]

Hibernate在进行读取数据的时候,根据缓存机制在相应的缓存中查询,如果在缓存中找到了需要的数据(我们把这称做“缓存命中”),则就直接把命中的数据作为结果加以利用,避免的了建立数据库查询的性能损耗。它是持久层性能提升的关键。


[b]Hibernate缓存分类[/b]
一级缓存: Session级别的缓存
事务范围 缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。缓存的介质是内存。事务可以是数据库事务或者应用事务,每个事务都有独自的缓存,缓存内的数据通常采用相互关联的的对象形式。一级缓存就属于事务范围。



二级缓存: SessionFactory级别的全局缓存
进程范围 缓存被进程范围内的所有事务共享。这些事务有可能并发访问缓存,因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于进程的生命周期,进程结束时,缓存也就结束了生命周期。它的物理介质可以是内存或硬盘。
集群范围 在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,缓存中的数据通常采用对象的松散数据形式。

持久化层的第二级缓存就存在于进程范围或集群范围

[b]hibernate一级缓存[/b]
理解一级缓存 (1)
Session具有一个缓存,是一块内存空间,在这个内存空间存放了相互关联的java对象,这种位于Session缓存内的对象也被称为持久化对象,Session负责根据持久化对象的状态变化来同步更新数据库。
Session的缓存是内置的,不能被卸除的,也被称为Hibernate的第一级缓存。在正常的情况下一级缓存是由Hibernate自动维护的,无需人工干预。

session缓存中对象的生命周期依赖session实例

理解一级缓存 (2)
(1)当应用程序调用Session接口的save()、update()、saveOrUpdate()时,如果Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。
(2)当调用Session接口的load()、get()以及Query查询接口的list()、iterator()方法时,如果Session缓存中存在相应的对象,就不需要到数据库中检索。
(3)当调用Session的close()时, Session缓存就被清空。


Session接口的用法 (1)
Session接口是Hibernate向应用程序提供的操纵数据库的最主要的接口,它提供了基本的保存、更新、删除和加载等方法。
Java对象在Hibernate持久化层的状态:
临时状态:刚用new语句创建,还没有被持久化,并且不处于session缓存中(处于临时状态的对象成为临时对象)
持久化状态:已经被持久化,并且加入到session缓存中。处于持久化状态的对象称为持久化对象
删除状态:不再处于session缓存中,并且session已经计划将其从数据库中删除
游离状态:已经被持久化,但不再处于session缓存中。处于游离状态的对象称为游离对象

get()和load() <重点>
试图从数据库加载一个实体对象时,Session先判断对象是否存在,如果存在就不到数据库中检索。返回的对象都位于Session缓存中,接下来修改了持久化对象的属性后,当Session清理缓存时,会根据持久化对象的属性变化来同步更新数据库。
区别:
(1)当数据库中不存在与OID对应的记录时,load()方法抛出ObjectNotFoundException异常,而get()方法返回null.
(2)两者采用不同的检索策略。
默认情况下,load()方法采用延迟检索策略(Hibernate不会执行select语句,仅返回实体类的代理类实例,占用内存很少);而get()采用立即检索策略(Hibernate会立即执行select语句)。
使用场合:
(1)如果加载一个对象的目的是为了访问它的各个属性,可以用get();
(2)如果加载一个对象的目的是为了删除它,或者建立与别的对象的关联关系,可以用load() ;

update() 和merge()
update()方法把游离对象加入当前Session缓存中,计划执行update语句。
当update()方法关联一个游离对象时,如果session缓存中已经有一个同类型且 ID相同的持久化对象,那么update()方法会抛出NonUniqueException异常.
当update()方法关联一个持久化对象时,该方法不起作用。
merge() 方法能够把游离对象的属性复制到一个持久化对象中。


saveOrUpdate()
同时包含了save()和update()方法的功能。
如果传入的是临时对象,就调用save()方法;
如果传入的是游离对象,就调用update()方法
如果传入的是持久化对象,就直接返回。
delete() :计划执行一个delete语句,把对象从Session缓存中删除。
close() :清空session缓存。

Session清理缓存
清理缓存是指Session按照缓存中对象的属性变化来同步更新数据库。
Session在清理缓存的时候会自动进行脏检查(dirty-check),如果发现Session缓存中的对象与数据库中相应的记录不一致,就会同步数据库。
Session是如何进行脏检查的呢?
当一个对象被加入到Session缓存时,Session 会为该对象的值类型的属性复制 一份快照。当Session清理缓存的时候,会进行脏检查,即比较对象的当前属性与它的快照,来判断对象的属性是否发生变化,如果发生变化,就称这个对象是“脏对象”,Session会根据脏对象的最新属性来执行相关的SQL语句,从而同步更新数据库。


Session何时清理缓存?
session缓存中对象的属性每次发生变化,Session不会立即清理缓存及执行相关的update语句,而是在特定的时间点才清理缓存,这使得Session能够把几条相关的sql语句合并为一条sql语句,以便减少访问数据库的次数。session会在下面的时间点清理缓存:  (1)当应用程序调用org.hibernate.Transaction的commit()方法的时候  commit()方法先清理缓存,然后再向数据库提交事务。
(2) 当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生变化,就会先清理缓存,使得Session缓存与数据库进行了同步,从而保证查询结果返回的是正确的数据。
(3) 当应用程序显式调用Session的flush()方法的时候


Sesion缓存的管理
第一级缓存在正常的情况下是由Hibernate自动维护的。在特殊的情况下需要我们进行手动维护,Hibernate就提供了两个管理Session缓存的方法:   (1)Session.evict(Object o)   将某个特定的对象从缓存中清除,使用此方法有两种适用情形,
一是在特定的操作(如批量处理),需要及时释放对象占用的内存。
二是不希望当前Session继续运用此对象的状态变化来同步更新数据 库。
(2)Session.clear() 清除缓存中的所有持久化对象。

注意:
1、在多数情况下并不提倡通过evit()和clear()来管理一级缓存。
2、管理一级缓存最有效的方法是采用合理的检索策略和检索方式来节省内 存的开销


Session缓存的作用
减少访问数据库的频率。
保证缓存中的对象与数据库中的相关记录保持同步。当缓存中的持久化对象的属性发生变化时,Session并不会立即执行相关的update语句,
Hibernate二级缓存

因为Session的生命期往往很短,存在于Session内部的第一级缓存的生命期当然也很短,所以第一级缓存的命中率是很低的。其对系统性能的改善也是很有限的。当然,这个Session缓存的主要作用是保持Session内部数据状态同步,并非是hibernate为了大幅提高系统性能所提供的。

Hibernate二级缓存:
SessionFactory级别的全局缓存
可配置可插拔的缓存插件
涵盖了进程范围与集群范围
物理介质:内存或硬盘

Hibernate的两级缓存机制:
如果在一级缓存中没有查询到相应的数据,可以到二级缓存中查找,如果在二级缓存中也没有找到数据,那么就只好查询数据库了。
Cache Providers(1)

Hibernate本身并不提供二级缓存的产品化实现,而是为众多支持Hibernate的第三方缓存插件提供整和接口。缓存插件用于把第三方的具体的缓存实现软件和Hibernate集成,可以在每个持久化类或每个集合的粒度上配置二级缓存。通过在hibernate.cache.provider_class属性中指定org.hibernate.cache.CacheProvider的某个实现的类名,你可以选择让Hibernate使用哪个缓存实现。Hibernate打包一些开源缓存实现,提供对它们的内置支持。
除此之外,你也可以实现你自己的缓存,将它们插入到系统中。

Hibernate支持以下类型的缓存插件:
为了把这些缓存插件集成到Hibernate中,Hibernate提供了org.hibernate.cache.CacheProvider接口,它是缓存插件和与Hibernate之间的适配器。Hibernate分别为这些缓存插件提供了内置的CacheProvider实现。


如果需要实现其它类型的缓存插件,只需要为这个插件提供实现org.hibernate.cache.CacheProvider接口的类。
缓存的并发访问策略:
当两个并发的事务同时访问数据库相同数据的时候,有可能出现五类并发访问问题,因此必须采用必要的事务隔离措施。同样,两个并发的事务同时访问缓存的相同数据时,也有可能出现各类并发问题。
因此必须为Hibernate二级缓存设定适当的并发访问策略,每一种策略对应一种事务隔离级别。
二级缓存的配置
配置二级缓存主要包含以下几个步骤:
启用二级缓存;
选择需要进行二级缓存的持久化类,设置它的缓存并发访问策略;
选择合适的缓存插件;
为命名缓存设置数据过期策略;

启用二级缓存:在Hibernate的配置文件中配置属性
hibernate.cache.use_second_level_cache = true
注意:此属性对指定<cache>的类缺省为true 。

选择需要二级缓存的持久化类,设置它的缓存并发访问策略
transactional|read-write|nonstrict-read-write|read-only
Hibernate允许在分散的各个映射文件中为持久化类设置二级缓存,也允许在Hibernate的配置文件中集中设置二级缓存,指定<class-cache>和 <collection-cache> 元素,统一配置。后一种方式更有利于代码维护。

在映射文件中设置二级缓存×××.hbm.xml :
类或者集合映射的“<cache>元素”的详细用法:
<cache
usage="transactional|read-write|nonstrict-read-write|read-only"
region="RegionName"
include="all|non-lazy" />
usage:(必须)说明了缓存的同步策略: transactional、 read-write、 nonstrict-read-write或 read-only。
region :(可选, 默认为类或者集合的名字) 指定二级缓存的区域名
include :(可选,默认为 all)如果取值为non-lazy,表示当缓存一个对象时,不会缓 存它的映射为延迟加载的属性。

例如:
<hibernate-mapping>
<class name="com.Dept" table=“T_DEPT" >
<cache usage=“ nonstrict-read-write” />
…..
<set name=“users” lazy=“true”>
<cache usage=“ nonstrict-read-write” />
……
</set>
</class>
</hibernate-mapping>



注意:
1、<cache>元素必须紧跟<class>元素
2、 Hibernate只会缓存对象的简单属性的值, 如果要缓存集合属性,必须在集合元素中也加入<cache>子元素,而Hibernate仅仅是把与当前持久对象关联的对象的ID存放到缓存中。如果希望把整个关联的对象的所有数据都存入缓存,则要在相应关联的对象的映射文件中配置<cache>元素。


注解方式配置hibernate二级缓存:(见ppt)

选择合适的缓存插件: 在配置文件指定CacheProvider

<property name=”hibernate.cache.provider_class”> org.hibernate.cache.EhCacheProvider </property>

每一种缓存插件都有自带的配置文件,需要手工编辑配置文件,在配置文件中需要为每个命名缓存设置数据过期策略.
如:Ehcache 插件 ehcache.xml
Jbosscache 插件 treecache.xml


以ehcache为例进行介绍。

ehcache.xml样例:

<ehcache updateCheck="false" dynamicConfig="false“>
<diskStore path="D:\\temp"/>
<!--设定缓存的默认数据过期策略 -->
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<cache name=“com.domain.Dept” maxElementsInMemory=“500“ eternal=”true“ timeToIdleSeconds=”0“
timeToLiveSeconds=”0“
overflowToDisk="true“/>
</ehcache>


配置文件ehcache.xml

把ehcache.xml放在classpath下,自动加载。
<diskStore>元素,指定一个文件目录,当ehcache把缓存写入硬盘的时候,就 把数据写到这个文件目录下。
<defaultCache>元素,设置缓存的默认数据过期策略
<cache>元素,设置具体的命名缓存的数据过期策略
<cache name=“com.domain.Dept” //持久化类全名 maxElementsInMemory=“500“ //基于内存的缓存可存放对象的最大数 目 eternal=”false“ //缓存中对象是否为永久的,如果是,超时设置将 被忽略,对象从不过期 timeToIdleSeconds=”120“ //允许对象处于空闲状态的最长时间(单 位秒)
timeToLiveSeconds=”120“ //对象允许在缓存中的最长时间(单位秒) overflowToDisk=”true“ //内存不足时,是否启用基于硬盘的缓存 />

每个命名缓存代表一个缓存区域,每个缓存区域都有自己的数据过期策略。每个需要缓存的持久化类都要这样配置。如果没有配置,hibernate会在启动的时候警告你,然后使用defaultCache的配置。


查看二级缓存的活动:
1、查看二级缓存的操作过程
调试时候,可以设置log4j的log4j.logger.org.hibernate.cache=debug(记录第二级缓存的活动),更方便看到ehcache的操作过程。用于调试过程,实际应用发布时候,请注释掉,以免影响性能。在log4j.properties文件中添加如下内容:
log4j.logger.org.hibernate.cache=debug


2、SessionFactory提供了查看二级缓存中的数据的方法
Map cacheEntries= sessionFactory.getStatistics()
.getSecondLevelCacheStatistics(“cacheRegionName”)
.getEntries();

一级缓存和二级缓存的交互(1)
一级缓存与二级缓存的几种交互模式,分别用org.hibernate.CacheMode类的5个静态常量表示。
CacheMode.NORMAL :从二级缓存读写数据(默认模式)
CacheMode.GET :读取模式,从二级缓存读数据
CacheMode.PUT : 写入模式,仅向二级缓存写数据,但不从二级缓存读数据
CacheMode.IGNORAL :忽略模式,不会从二级缓存中读取数据,也不会向其中写入数据。
CacheMode.REFRESH :刷新模式,Session不会从二级缓存中读取数据,但会向其中写 入从数据库中读到的数据。


Session接口设置交互模式的方法session.setCacheMode():
session = sessionFactory.openSession();
//设置了不把数据放入二级缓存,仅从二级缓存读数据
session.setCacheMode(CacheMode.GET);
tx = session.beginTransaction();
Dept dept = (Dept)session.get(Dept.class,"001");
System.out.println(dept.getDeptName());
//查询出来后没有放入二级缓存
session.getTransaction().commit();
session.close();


hibernate查询缓存
查询缓存概述
Hibernate的二级缓存策略是针对ID查询的策略,和对象ID密切相关,那么对于条件查询就不怎么适用了。对于这种情况的存在,Hibernate引入了“查询缓存”,在一定程度上缓解这个问题。
启用查询缓存后,第一次执行查询时,Hibernate会把查询结果放入查询缓存中。以后再次执行该查询语句时,只需从缓存中获得查询结果,从而提高查询性能。
查询缓存依赖二级缓存。



启用查询缓存
对查询语句启用查询缓存的步骤
1、启用二级缓存
hibernate.cache.use_second_level_cache=true

2、在配置文件中设置查询缓存属性为true
hibernate.cache.use_query_cache=true

调用Query接口的setCacheable(true)
Query query = session.createQuery(hql);
query.setCacheable(true); //启用查询缓存
query.setCacheRegion(“queryCacheRegion”); //设置查询缓存区域(数据过期策略)
query.list();

查询缓存小结
(1) 对于查询缓存来说,缓存的 key 是根据 hql 生成的 sql ,再加上参 数,分页等 信息。
(2) 查询缓存只会存放实体的ID,对于以下HQL查询语句
FROM Dept ;
如果启用了查询缓存,Hibernate会把查询结果中Dept对象的ID属性存放到查询 缓存中,把Dept对象放入Dept缓存中。
第二次执行查询的时候,如果查询缓存还没有过期,就会从查询缓存中获取一连 串的ID值,然后根据每个ID到Dept缓存中查找相应的dept对象。
因此,class缓存的超时时间不能短于查询缓存设置的超时时间!

我们从查询缓存的策略中可以看出,Query Cache只是在特定的条件下才会发挥作用,而且要求相当严格:   (1) 完全相同的HQL重复执行。   (2) 重复执行期间,Query Cache对应的数据表不能有数据变动(比如添、删、改操作) 绝大多数的查询并不能从查询缓存中受益,所以Hibernate默认是不进行查询缓存 的。
查询缓存适用于以下场合:
(1) 在应用程序运行时经常使用的查询语句(参数相同)
(2)很少对与查询语句检索到的数据进行插入、删除或更新操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值