Hibernate二级/查询缓存的陷阱
这篇文章将介绍如何设置Hibernate二级和查询缓存,它们如何工作以及最常见的陷阱。
休眠二级缓存是用于存储实体数据的应用程序级缓存。 查询缓存是一个单独的缓存,仅存储查询结果。
这两个缓存实际上是并存的,因为在很多情况下,我们都希望在没有其他缓存的情况下使用它们。 如果使用得当,这些缓存可以通过减少命中数据库的SQL语句的数量,以透明的方式提高性能。
第二级缓存如何工作?
第二级缓存存储实体数据,但不存储实体本身。 数据以“脱水”格式存储,该格式看起来像哈希图,其中键是实体ID,而值是原始值列表。
这是有关二级缓存内容的外观的示例:
*-----------------------------------------*
| Person Data Cache |
|-----------------------------------------|
| 1 -> [ "John" , "Q" , "Public" , null ] |
| 2 -> [ "Joey" , "D" , "Public" , 1 ] |
| 3 -> [ "Sara" , "N" , "Public" , 1 ] |
*-----------------------------------------*
当通过Id从数据库entityManager.find()
例如,使用entityManager.find()
从对象加载对象时,或者遍历惰性初始化关系时,将填充第二级缓存。
查询缓存如何工作?
查询高速缓存在概念上看起来像一个哈希图,其中键由查询文本和参数值组成,并且值是与查询匹配的实体ID的列表:
*----------------------------------------------------------*
| Query Cache |
|----------------------------------------------------------|
| ["from Person where firstName=?", ["Joey"] ] -> [1, 2] ] |
*----------------------------------------------------------*
有些查询不返回实体,而是仅返回原始值。 在那些情况下,值本身将存储在查询缓存中。 执行可缓存的JPQL / HQL查询时,将填充查询缓存。
这两个缓存之间有什么关系?
如果正在执行的查询先前已缓存了结果,则不会有SQL语句发送到数据库。 而是从查询缓存中检索查询结果,然后使用缓存的实体标识符访问第二级缓存。
如果第二级缓存包含给定ID的数据,它将重新水化实体并返回它。 如果第二级缓存不包含该特定ID的结果,则将发出SQL查询以从数据库中加载实体。
如何在应用程序中设置两个缓存
第一步是在类路径中包含hibernate-ehcache
jar:
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-ehcache</artifactId><version>SOME-HIBERNATE-VERSION</version>
</dependency>
需要将以下参数添加到EntityManagerFactory
或SessionFactory
的配置中:
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="net.sf.ehcache.configurationResourceName">/your-cache-config.xml</prop>
首选使用EhCacheRegionFactory
而不是SingletonEhCacheRegionFactory
。 使用EhCacheRegionFactory
意味着Hibernate将为Hibernate缓存创建单独的缓存区域,而不是尝试重用应用程序中其他位置定义的缓存区域。
下一步是在文件your-cache-config.xml
配置缓存区域设置:
<?xml version="1.0" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"updateCheck="false"xsi:noNamespaceSchemaLocation="ehcache.xsd" name="yourCacheManager"><diskStore path="java.io.tmpdir"/><cache name="yourEntityCache"maxEntriesLocalHeap="10000"eternal="false"overflowToDisk="false"timeToLiveSeconds="86400" /><cache name="org.hibernate.cache.internal.StandardQueryCache"maxElementsInMemory="10000"eternal="falsetimeToLiveSeconds="86400"overflowToDisk="false"memoryStoreEvictionPolicy="LRU" /><defaultCachemaxElementsInMemory="10000"eternal="false"timeToLiveSeconds="86400"overflowToDisk="false"memoryStoreEvictionPolicy="LRU" />
</ehcache>
如果未指定缓存设置,则采用默认设置,但是最好避免这种情况。 通过在ehcache
元素中填充name
属性,确保为缓存命名。
为缓存指定一个名称可以防止它使用默认名称,该名称可能已经在应用程序的其他位置使用过。
使用二级缓存
现在可以使用二级缓存了。 为了缓存实体,请使用@org.hibernate.annotations.Cache
注释对它们进行注释:
@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="yourEntityCache")
public class SomeEntity {...
}
关联也可以由二级缓存进行缓存,但是默认情况下不这样做。 为了启用关联的缓存,我们需要将@Cache
应用于关联本身:
@Entity
public class SomeEntity {@OneToMany@Cache(usage=CacheConcurrencyStrategy.READ_ONLY,region="yourCollectionRegion")private Set<OtherEntity> other;
}
使用查询缓存
配置查询缓存后,默认情况下尚未缓存任何查询。 需要将查询明确标记为已缓存,例如,这是如何将命名查询标记为已缓存:
@NamedQuery(name="account.queryName",query="select acct from Account ...",hints={@QueryHint(name="org.hibernate.cacheable",value="true")}
})
这是将条件查询标记为已缓存的方法:
List cats = session.createCriteria(Cat.class).setCacheable(true).list();
下一节将介绍一些在尝试设置这两个缓存时可能遇到的陷阱。 这些行为按设计工作,但仍然令人惊讶。
陷阱1 –查询缓存会降低性能,导致大量查询
如果将缓存的查询结果配置为比查询返回的缓存的实体更频繁地过期,则会发生两个缓存的工作方式的有害副作用。
如果查询已缓存结果,它将返回实体ID的列表,然后针对第二级缓存进行解析。 如果具有这些ID的实体未配置为可缓存或它们已过期,则每个实体ID的选择将命中数据库。
例如,如果一个缓存的查询返回了1000个实体ID,而第二级缓存中没有这些实体,则将对数据库发出1000个ID选择。
该问题的解决方案是将查询结果到期配置为与查询返回的实体的到期保持一致。
陷阱2 –与
当前无法为同一父实体的不同子类指定不同的缓存策略。
例如,这将不起作用:
@Entity
@Inheritance
@Cache(CacheConcurrencyStrategy.READ_ONLY)
public class BaseEntity {...
}@Entity
@Cache(CacheConcurrencyStrategy.READ_WRITE)
public class SomeReadWriteEntity extends BaseEntity {...
}@Entity
@Cache(CacheConcurrencyStrategy.TRANSACTIONAL)
public class SomeTransactionalEntity extends BaseEntity {...
}
在这种情况下,仅考虑父类的@Cache
注释,并且所有具体实体都具有READ_ONLY
并发策略。
陷阱3 –使用基于单例的缓存时,缓存设置将被忽略
建议将缓存区域工厂配置为EhCacheRegionFactory
,并通过net.sf.ehcache.configurationResourceName
指定ehcache配置。
此区域工厂的替代方法是SingletonEhCacheRegionFactory
。 对于该区域工厂,使用缓存名称作为查找键将缓存区域存储为单例。
单例区域工厂的问题在于,如果应用程序的另一部分已经在单例中注册了具有默认名称的缓存,则这将导致忽略通过net.sf.ehcache.configurationResourceName
传递的ehcache配置文件。
结论
如果设置正确,则二级缓存和查询缓存非常有用,但是要记住一些陷阱,以免发生意外行为。 总而言之,这是一个透明工作的功能,如果使用得当,可以显着提高应用程序的性能。
翻译自: https://www.javacodegeeks.com/2014/06/pitfalls-of-the-hibernate-second-level-query-caches.html
Hibernate二级/查询缓存的陷阱相关推荐
- hibernate的查询缓存
在hibernate的使用中,大家多数时间都在讨论一级缓存和二级缓存,而往往忽略了查询缓存.其实hibernate的查询缓存在使用过程中也起着同样重要的作用.hibernate的查询缓存是主要是针对普 ...
- 【HIBERNATE框架开发之九】HIBERNATE 性能优化笔记!(遍历、一级/二级/查询/缓存、乐观悲观锁等优化算法)...
本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/hibernate/825.html 1. ...
- 【Hibernate框架开发之九】Hibernate 性能优化笔记!(遍历、一级/二级/查询/缓存/乐观悲观锁等优化算法)...
1. 循环分页或者循环进行部分读取处理数据的时候,使用 session.clear() ; 2. 对应1+N(N+1)问题使用如下解决方式: 1): 使用createCriteria进行查询 ...
- Hibernate 二级缓存
Hibernate 缓存 •缓存(Cache): 计算机领域非常通用的概念.它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提 ...
- Hibernate二级缓存问题
相关概念和定义 1.缓存的意义 把一些不常修改,但是又经常用的数据存放到内存中,这样能减少与数据库的交互,提升程序的性能 2.Hibernate中提供了两级缓存: 第一级别的缓存是Session级别的 ...
- Hibernate 缓存机制续 - 查询缓存
对于一个应用来说,最多的操作是查询,而并非是写入和更改,如果能将查询缓存起来,那么能够有效提升效率. Hibernate的查询缓存是基于二级缓存的,所以,如果想使用查询缓存,必须先开启二级缓存. 1. ...
- hibernate二级缓存(三) 自定义实现一个简单的hibernate二级缓存
hibernate二级缓存(三) 自定义实现一个简单的hibernate二级缓存 前面我们已经提及过hibernate内部为二级缓存的扩展做了很多的实现.我们只需要实现RegionFactoryTem ...
- ssh整合hibernate 使用spring管理hibernate二级缓存,配置hibernate4.0以上二级缓存
ssh整合hibernate 使用spring管理hibernate二级缓存,配置hibernate4.0以上二级缓存 hibernate : Hibernate是一个持久层框架,经常访问物理数据库 ...
- java ssm框架 缓存_SSM框架之MyBatis3专题4:查询缓存
查询缓存的使用,主要是为了提高查询访问速度.将用户对同一数据的重复查询过程简化,不再每次均从数据库中查询获取结果数据,从而提高访问速度. MyBatis的查询缓存机制,根据缓存区的作用域(声明周期)可 ...
最新文章
- 每日一皮:多线程理想与现实的差距
- python网页爬虫-Python 爬虫网页内容提取工具xpath
- java HelloWorld 编程风格实践
- 如何判断服务器之间的服务是否可用?ping 还是 telnet?
- mysql+keepalived必须要lvs吗_MySQL 双主热备 + LVS + Keepalived 高可用操作记录
- 304 Not Modify
- PL/SQL Developer使用技巧总结
- 2009年1月5日 洛基网络教室听课笔记
- 计算机病毒教学评课,计算机病毒评课稿.docx
- SQL中自增(AUTO_INCREMENT)字段介绍
- Android之Retrofit详解(转载)
- 计算机应用课程的考核情况,《计算机应用技术》课程kpi考核说明..doc
- Java八大基本数据类型
- java边缘检测_Sobel边缘检测实现
- 复制文件并重命名到新的文件夹
- 赋能智慧交通的5G关键技术
- 谷歌在中国大陆停止谷歌翻译服务,Chrome “翻成中文”无法使用
- 【元宇宙系列】元宇宙的创世居民——M 世代(Mateverse)
- DevOps八荣八耻了解下,哈哈~
- 阿里云服务器建站怎么上传文件?
热门文章
- 法在计算机课程中的应用,尝试教学法在中职《计算机应用基础》课程中的应用(原稿)...
- 如何获得即时编译器(JIT)的汇编代码(linux环境下)
- 为什么jdk的CLASSPATH环境变量需要设置rt.jar 和 tools.jar
- 无处不再的广告_我的机器人现在无处可去。 无家可归。 无服务器。
- 突破极限–如何使用AeroGear Unified Push for Java EE和Node.js
- Spring@懒惰注释
- 将Amazon Cognito与单页面应用程序(Vue.js)集成
- lambda 函数式编程_Java 8 Lambda表达式的函数式编程– Monads
- 借助Web技术,桌面用户界面将保持活跃
- @async方法不调用了_在Spring中使用Future对象调用Async方法调用