Spring Data JPA默认方法无需@QueryHints使用Hibernate二级缓存
在Spring Boot应用中使用Spring Data JPA,为部分实体启用Hibernate二级缓存,应用采用Redis作为缓存服务,使用支持Spring Boot集成的Redisson作为缓存实现,现有配置如下:
实体类配置:
@Entity @Cacheable @Cache(region = "baseInfoCache", usage = CacheConcurrencyStrategy.READ_WRITE) public class BaseInfo { }
application.yml配置:
spring: jpa: properties: hibernate: cache: use_query_cache: true use_second_level_cache: true use_structured_entries: true region_prefix: dd_hibernate region: factory_class: org.redisson.hibernate.RedissonRegionFactory redisson: fallback: true config: redisson/redisson-dev.yaml
redisson-dev.yml配置:
singleServerConfig: address: "redis://localhost:6379"
现象说明:
BaseInfoRepository继承JPA标准CrudRepository,首次调用findAll()方法后,Redis中可观测到对应缓存生成,缓存写入逻辑正常- 后续再次调用
findAll()时,Hibernate仍输出select查询日志,未从缓存加载数据 - 手动重写Repository中
findAll()方法并添加@QueryHints注解后,Hibernate可正常从缓存读取数据
需求:不使用@QueryHints注解、不重写Spring Data提供的原生默认方法,让默认Repository方法直接使用Hibernate二级缓存,确认该需求是否可实现,以及具体配置方式。
该需求完全可以实现。
缓存不命中的核心原因:Hibernate二级缓存分为实体实例缓存和查询结果缓存两部分,实体类上的@Cacheable注解仅开启了按主键查询的实体实例缓存,而findAll()这类全量、条件查询走查询结果缓存通道,Spring Data JPA默认生成的查询不会自动携带「查询结果可缓存」标记,因此即使缓存中已有数据,Hibernate仍会执行SQL查库。手动添加@QueryHints本质是给对应查询加上org.hibernate.cacheable = true的标记,触发查询缓存命中。
无需重写方法、无需逐个加注解,通过以下配置即可实现全局生效:
- 若使用Spring Boot 2.7及以上版本(对应Spring Data JPA 2.7+、Hibernate 5.6+),直接在原有
application.yml的JPA配置下新增全局配置即可:
spring: jpa: properties: hibernate: # 原有缓存配置全部保留,新增以下配置 jpa: query: cacheable: true
该配置是Hibernate提供的原生全局开关,开启后所有框架自动生成的JPA查询(包括Repository默认的findAll()、findAll(Example)、条件查询等方法)都会默认标记为可缓存,直接命中查询缓存,无需额外注解。
- 若使用版本低于2.7,无上述全局配置项,可通过自定义全局Repository扩展实现:
- 自定义
JpaRepositoryFactoryBean子类,重写Repository创建逻辑,给所有查询方法默认追加缓存查询提示 - 在启动类的
@EnableJpaRepositories注解中指定自定义的repositoryFactoryBeanClass,全局生效后所有Repository的默认方法都会自动携带缓存提示,无需逐个重写。
- 自定义
补充说明:全局开启查询缓存后,建议给不同业务实体的缓存区域设置合理的过期时间、最大容量,避免冷数据长期占用Redis内存;实体类上的
@Cacheable、@Cache注解需要保留,这是实体缓存正常生效的基础。
内容的提问来源于stack exchange,提问作者hamed

