JPA NamedQuery传新参数查询被缓存忽略返回旧结果问题
问题根因
该故障确实由JPA二级缓存配置错误导致,和你将缓存过期时间设为1ms后查询恢复正常的现象完全匹配。
你当前使用的@Cache(expiry = 300000, size = 10000)是EclipseLink的实体级二级缓存注解,仅对按主键ID查询的实体生效,未针对按zipCode查询的命名查询配置正确的查询缓存规则:框架执行该命名查询时,不会将传入的zipCode参数作为缓存键的组成部分,第一次查询得到实体存入缓存后,后续执行同一条命名查询时会直接返回缓存中存储的第一条实体结果,完全忽略传入的不同参数值,最终导致无论传什么邮编都返回第一次查询的站点ID。
修复方案
可根据实际业务场景选择以下任意一种方案修复:
- 方案1:为命名查询显式开启带参数维度的查询缓存
在@NamedQuery配置中增加查询提示,让框架自动将绑定的zipCode参数作为缓存键的一部分,不同邮编的查询结果独立缓存,既保留5分钟缓存的性能收益,也不会出现结果串乱的问题:
@NamedQuery( name = StationZipBE.FIND_BY_ZIP_CODE, query = "select z from StationZipBE z where z.zipCode = :zipCode", hints = { @QueryHint(name = "eclipselink.query-results-cache", value = "true"), @QueryHint(name = "eclipselink.query-results-cache.expiry", value = "300000") } )
- 方案2:配置该命名查询绕过二级缓存
如果该查询的调用频率不高,不需要缓存加速,可以直接配置查询不读取二级缓存,每次直接访问数据库获取结果,实体级别的缓存仍会正常生效:
方式一:在命名查询上加注解提示
方式二:在业务代码构造查询时设置提示@QueryHint(name = "eclipselink.cache-usage", value = "DoNotCheckCache")final Query namedQuery = getEntityManager().createNamedQuery(StationZipBE.FIND_BY_ZIP_CODE); namedQuery.setParameter("zipCode", zipCode); namedQuery.setHint("eclipselink.cache-usage", "DoNotCheckCache"); - 方案3:补全库表约束从根源规避缓存异常
从业务逻辑看一个ZIP_CODE应当唯一对应一个站点,可给station_zip表的ZIP_CODE字段加唯一索引,同时在实体类的zipCode字段上标记@Column(unique = true),JPA识别到字段的唯一属性后,会自动将该字段作为实体缓存的辅助键,不会再出现跨参数的缓存命中错误。
修复完成后可传入多个不同的存在的邮编验证,无需将缓存过期时间设为1牺牲性能,即可返回正确匹配的站点ID。
内容的提问来源于stack exchange,提问作者Gerd
相关产品推荐
相关产品推荐

