hibernate之高速缓存基本原理(高速缓存实践)
选择并发控制策略
如例:
Category只有少量的实例并且很少更新,而且实例在许多用户之间共享。它是供二级高速缓存使用的一个很好的备选类。从添加hibernate高速缓存Category实例所需的映射元素开始:
<class name="auction.model.Category" table="CATEGORY"> <cache usage="read-write"/> <id>...</id> ... </class>
usage="read-write"属性告诉hibernate,给auction.model.Category高速缓存使用一个读/写并发策略。每当导航到Category,或者当按标识符加载Category时,hibernate现在就命中二级高速缓存。
类高速缓存始终对持久化类的整个层次结构而被启用。你无法只高速缓存一个特定子类的实例。
这个映射足以告诉hibernate去高速缓存所有简单的Category属性值,而不是被关联的实体或者集合状态。集合需要它们自己的<cache>区域。如例,给items集合使用read-write并发策略:
<class name="auction.model.Category" table="CATEGORY"> <cache usage="read-write"/> <id>...</id> ... <set name="items"> <cache usage="read-write"/> <key .../> </set> </class>
调用aCategory.getItems()时,这个高速缓存设置生效---换句话说,集合高速缓存是一个包含"哪些项目处在哪些类别中"的区域。它是只有标识符的高速缓存,在该区域中没有实际的Category或者Item数据。
如果你需要Item实例本身被高速缓存,就必须启用Item类的高速缓存。读/写(read-write)策略尤其适合。用户可不想根据可能失效的Item数据做出决定。更进一步,考虑bids的集合:bids集合中的特定Bid是不可变的,但是bids的集合是可变的,并且并发的工作单元需要毫不延迟地看到集合元素的添加或者移除:
<class name="Item" table="ITEM"> <cache usage="read-write"/> <id>...</id> ... <set name="bids"> <cache usage="read-write"/> <key..... </set> </class>
给Bid类应用只读(read-only)策略:
<class name="Bid" table="BID" mutable="false"> <cache usage="read-only"/> <id.... </class>
因此,Bid数据在高速缓存中从来不会过期,因为它只能被创建而从不更新。如果Bid实例被删除,Hibernate也从高速缓存中移除数据,但是不对这种做法提供任何事务的保证。
----------
理解高速缓存区域
hibernate在不同的高速缓存区域中保存不同的类/集合。区域是一个具名的高速缓存:这个句柄使你可以通过它在高速缓存提供程序配置中引用类和集合,并设置适用于该区域的过期策略。一种更为图形化的描述是,区域是一桶桶的数据,它们有两种类型:一种区域类型包含实体实例的分解数据,另一种类型只包含通过集合而被链接的实例标识符。
对于类高速缓存而言,区域的名称是类名;对于集合高速缓存而言,是类名加属性名。Category实例被高速缓存在具名
auction.model.Category的区域中,而items集合则被高速缓存在具名auction.model.Category.items的区域中。
hibernate具名hibernate.cache.region_prefix的配置属性可能被用来给特定的SessionFactory或者持久化单元指定区域名前缀。例如,如果前缀设置为dbl,Category就被高速缓存在具名dbl.auction.model.Category的区域中。如果应用程序使用多个SessionFactory实例或者持久化单元,这项设置就是必需的。没有它,不同持久化单元的高速缓存区域名称就可能冲突。
----------
设置本地的高速缓存提供程序
需要设置选择了高速缓存提供程序的配置属性:
hibernate.cache.provider_class = org.hibernate.cache.EhCacheProvider
这个例子中选择EHCache作为二级高速缓存。
现在,你需要指定高速缓存区域的属性。EHCache有自己的配置文件ehcache.xml,在应用程序的类路径中。hibernate发行包中为所有绑定的高速缓存提供程序捆绑了示例配置文件,因此我们建议你阅读那些文件中的使用注释,看看详细的配置,并给我们没有明确提到的所有选项假设默认值。
ehcache.xml中用于Category类的高速缓存配置可能看起来像这样:
<cache name="auction.model.Category" maxElementsInMemory="500" eternal="true" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false"/>
另一方面,Bid很小并且不可变,但是有很多,因此你必须配置EHCache来小心管理高速缓存内存消耗。你把过期超时和最大高速缓存大小限制都用上了:
<cache name="auction.model.Bid" maxElementsInMemory="50000" eternal="false" timeToIdleSeconds="1800" timeToLiveSeconds="100000" overflowToDisk="false"/>
----------
控制二级高速缓存
hibernate有一些有用的方法,可以帮助你测试和调优高速缓存。给二级高速缓存hibernate.cache.use_second_livel_cache考虑全局的配置转换。默认情况下,映射文件中的任何<cache>元素都触发二级高速缓存,并在启动时加载高速缓存提供程序。如果想要全局地禁用二级高速缓存,而不移除高速缓存映射元素或者注解,那么就设置这个配置属性为false。
就像Session提供通过编程来控制持久化上下文一级高速缓存的方法一样,SessionFactory对二级高速缓存也一样。可以调用evict(),通过指定类和对象标识符值,从二级高速缓存中移除元素:
SessionFactory.evict(Category.class,new Long(123));
也可以通过指定一个区域名称,来清除一个特定类的所有元素,或者清除一个特定集合角色:
SessionFactory.evict("auction.model.Category");
你将很少需要用到这些控制机制。还要注意二级高速缓存的清除是非事务的。也就是说,高速缓存区域在清除期间没有被锁定。
Hibernate还提供CacheMode选项,它可以由特定的Session启用。想像你想要在Session中把许多个对象插入到数据库。需要以批量来完成这项工作,以避免内存耗尽---每一个对象都被添加到一级高速缓存中。然而,如果为实体类启用了它,它也被添加到二级高速缓存中。CacheMode控制hibernate与二级高速缓存的交互:
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); session.setCacheMode(CacheMode.IGNORE); for(int i=0;i<100000;i++){ Item item = new Item(...); session.save(item); if(i % 100 ==0){ session.flush(); session.clear(); } } tx.commit(); session.close();
设置CacheMode.IGNORE告诉hibernate不要在这个特定的Session中与二级高速缓存交互。可用的选项如下:
. CacheMode.NORMAL---默认的行为。
. CacheMode.IGNORE---hibernate从来不与二级高速缓存交互,除了更新发生时使用被高速缓存的项目失效之外。
. CacheMode.GET---hibernate可能从二级高速缓存中读取项目,但不添加项目,除了更新发生时使项目失效之外。
. CacheMode.PUT---hibernate从来不从二级高速缓存中读取项目,但是当它从数据库中读取项目时,会把项目添加到高速缓存。
. CacheMode.REFRESH---hibernate从来不从二级高速缓存中读取项目,但是当它从数据库中读取项目时,会把项目
添加到高速缓存。在这种模式下,hibernate.cache.use_minimal_puts的作用被忽略,以便在一个复制的集群高速缓存中强制高速缓存刷新。
除了NORMAL和IGNORE之外,好的用例很少。
这样就结束了对hibernate应用程序中一级和二级高速缓存的讨论。我们想要重申一项在本节一开始时提到过的声明:你的应用程序不用二级高速缓存也应该执行得令人满意。如果你通过启用二级高速缓存,使应用程序中的一个特定过程在2秒而不是50秒内运行,你就只是治了标,而没有治本。抓取计划和择取策略的定制始终是你的第一个优化步骤;然后,通过二级高速缓存使应用程序更迅速,并把它扩展到它将必须在产品中处理的并发事务加载。