hibernate中的Cache管理
Hibernate中实现了良好的Cache机制,我们可以借助Hibernate内部的Cache迅速提高系统数据读取性能。
需要注意的是:Hibernate做为一个应用级的数据访问层封装,只能在其作用范围内保持Cache中数据的的有效性,也就是说,在我们的系统与第三方系统共享数据库的情况下,Hibernate的Cache机制可能失效。一个很简单的例子,如果你用access修改了库中的值,那么这就不会更新JVM中的缓冲池,这就导致了赃数据的产生。
Hibernate在本地JVM中维护了一个缓冲池,并将从数据库获得的数据保存到池中以供下次重复使用(如果在Hibernate中数据发生了变动,Hibernate同样也会更新池中的数据版本)。此时,如果有第三方系统对数据库进行了更改,那么,Hibernate并不知道数据库中的数据已经发生了变化,也就是说,池中的数据还是修改之前的版本,下次读取时,Hibernate会将此数据返回给上层代码,从而导致潜在的问题。外部系统的定义,并非限于本系统之外的第三方系统,即使在本系统中,如果出现了绕过Hibernate数据存储机制的其他数据存取手段,那么Cache的有效性也必须细加考量。如,在同一套系统中,基于Hibernate和基于JDBC的两种数据访问方式并存,那么通过JDBC更新数据库的时候,Hibernate同样无法获知数据更新的情况,从而导致脏数据的出现。
基于Java的Cache实现,最简单的莫过于HashTable,hibernate提供了基于Hashtable的Cache实现机制,不过,由于其性能和功能上的局限,仅供开发调试中使用。同时,Hibernate还提供了面向第三方Cache实现的接口,如JCS、EHCache、OSCache、JBossCache、SwarmCache等。
Hibernate中的Cache大致分为两层,第一层Cache在Session实现,属于事务级数据缓冲,一旦事务结束,这个Cache也就失效。此层Cache为内置实现,无需我们进行干涉。第二层Cache,是Hibernate中对其实例范围内的数据进行缓存的管理容器。
我们主要学习第二层Cache。
Hibernate早期版本中采用了JCS(JavaCachingSystem-ApacheTurbine项目中的一个子项目)作为默认的第二层Cache实现。由于JCS的发展停顿,以及其内在的一些问题(在某些情况下,可能导致内存泄漏以及死锁),新版本的Hibernate已经将JCS去除,并用EHCache作为其默认的第二级Cache实现。相对JCS,EHCache更加稳定,并具备更好的缓存调度性能,缺陷是目前还无法做到分布式缓存,如果我们的系统需要在多台设备上部署,并共享同一个数据库,必须使用支持分布式缓存的Cache实现(如JCS、JBossCache)以避免出现不同系统实例之间缓存不一致而导致脏数据的情况。Hibernate对Cache进行了良好封装,透明化的Cache机制使得我们在上层结构的实现中无需面对繁琐的Cache维护细节。
目前Hibernate支持的Cache实现有:
HashTable:net.sf.hibernate.cache.HashtableCacheProvider支持查询缓冲。
EHCache:net.sf.ehcache.hibernate.Provider支持查询缓冲。
OSCache:net.sf.hibernate.cache.OSCacheProvider支持查询缓冲。
SwarmCache:net.sf.hibernate.cache.SwarmCacheProvider支持集群。
JBossCache:net.sf.hibernate.cache.TreeCacheProvider支持集群。
其中SwarmCache提供的是invalidation方式的分布式缓存,即当集群中的某个节点更新了缓存中的数据,即通知集群中的其他节点将此数据废除,之后各个节点需要用到这个数据的时候,会重新从数据库中读入并填充到缓存中。而JBossCache提供的是Reapplication式的缓冲,即如果集群中某个节点的数据发生改变,此节点会将发生改变的数据的最新版本复制到集群中的每个节点中以保持所有节点状态一致。
使用第二层Cache,需要在hibernate的配置文件进行配置(省略)主要介绍一下cache策略
cache策略可选值有以下几种:
1.read-only只读。
2.read-write可读可写。
3.nonstrict-read-write如果程序对并发数据修改要求不是非常严格,只是偶尔需要更新数据,可以采用本选项,以减少无谓的检查,获得较好的性能。
4.transactional事务性cache。在事务性Cache中,Cache的相关操作也被添加到事务之中,如果由于某种原因导致事务失败,我们可以连同缓冲池中的数据一同回滚到事务开始之前的状态。目前Hibernate内置的Cache中,只有JBossCache支持事务性的Cache实现。
其他参数简介:
maxElementsInMemory="10000"//Cache中最大允许保存的数据数量
eternal="false"//Cache中数据是否为常量
timeToIdleSeconds="120"//缓存数据钝化时间
timeToLiveSeconds="120"//缓存数据的生存时间
overflowToDisk="true"//内存不足时,是否启用磁盘缓存
需要注意的是Hibernate的数据库查询机制。我们从查询结果中取出数据的时候,用的最多的是两个方法:Query.list();Query.iterate();
对于list方法而言,实际上Hibernate是通过一条SelectSQL获取所有的记录。并将其读出,填入到POJO中返回。
而iterate方法,则是首先通过一条SelectSQL获取所有符合查询条件的记录的id,再对这个id集合进行循环操作,通过单独的SelectSQL取出每个id所对应的记录,之后填入POJO中返回。
也就是说,对于list操作,需要一条SQL完成。而对于iterate操作,需要n+1条SQL。
看上去iterate方法似乎有些多余,但在不同的情况下确依然有其独特的功效,如对海量数据的查询,如果用list方法将结果集一次取出,内存的开销可能无法承受。另一方面,对于我们现在的Cache机制而言,list方法将不会从Cache中读取数据,它总是一次性从数据库中直接读出所有符合条件的记录。而iterate方法因为每次根据id获取数据,这样的实现机制也就为从Cache读取数据提供了可能,hibernate首先会根据这个id在本地Cache内寻找对应的数据,如果没找到,再去数据库中检索