You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate多对一映射缓存机制解析及Station查询缓存失效问题

Hibernate多对一映射缓存机制及实际问题解决

一、Hibernate多对一映射的缓存机制

Hibernate的多对一映射缓存分为两级,工作逻辑如下:

  • 一级缓存(Session级):默认生效,同一个Session内,多次获取同一个关联实体(比如通过Station取Company),第一次查库后后续直接从缓存读取,不会重复查库。
  • 二级缓存(全局级):需手动配置,若关联实体(如Company)开启二级缓存,跨Session获取该实体时会优先从二级缓存读取。但注意:实体的@Cache注解仅缓存单个实体实例(按主键),不会自动缓存自定义查询的结果集合。
  • 关联查询的缓存逻辑:多对一关联本身不会缓存反向查询结果(比如通过Company查Station列表),这类自定义查询的缓存需要单独配置查询缓存。

二、实际场景问题排查与解决

你的问题核心是:Station实体的@Cache注解仅缓存单个Station实例,而findByCompany这种自定义查询的结果并未被缓存,所以每次调用都会命中数据库。具体修正步骤如下:

问题根源

  1. @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)作用是缓存Station实体(按主键查询时生效),但不覆盖自定义查询的结果缓存。
  2. 自定义查询的缓存需要单独开启并配置。
  3. 实体类存在两处不合理配置:
    • @JoinColumn(name = "company_id" , unique =true):unique=true会强制一个Company只能对应一个Station,与多对一的业务逻辑矛盾(多对一应为多个Station关联同一个Company),需删除该属性。
    • @Fetch(value = FetchMode.JOIN)与FetchType.LAZY冲突:FetchMode.JOIN是立即加载策略,会覆盖LAZY配置,导致加载Station时自动查库获取Company,浪费性能。

解决步骤

  1. 开启查询缓存:在项目配置文件中添加Hibernate查询缓存开关(以Spring Boot的application.properties为例):
    spring.jpa.properties.hibernate.cache.use_query_cache=true
    
  2. 给自定义查询方法添加缓存配置:在StationRepository的findByCompany方法上添加查询缓存注解:
    import org.hibernate.annotations.QueryHints;
    import javax.persistence.QueryHint;
    import java.util.List;
    
    public interface StationRepository extends JpaRepository<Station, Long> {
        @QueryHints(@QueryHint(name = QueryHints.CACHEABLE, value = "true"))
        List<Station> findByCompany(Company company);
    }
    
  3. 修正Station实体的错误配置:
    @Table(name = "station")
    @Setter
    @Getter
    @Builder
    @Cacheable
    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public class Station {
        public Station() {}
        @Id
        @SequenceGenerator(name = "mySeqGen", sequenceName = "station_seq", allocationSize = 1)
        @GeneratedValue(generator = "mySeqGen")
        @Column(name = "station_id")
        private Long id;
        
        Double latitude;
        Double longitude;
        
        @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        @JoinColumn(name = "company_id")
        // 移除@Fetch(FetchMode.JOIN),保持懒加载生效
        private Company company;
    }
    
  4. 验证缓存效果:再次调用findStations方法,首次执行会查库,后续调用将直接从缓存读取查询结果及Station实体,不再命中数据库。

内容的提问来源于stack exchange,提问作者Hamdhan Azeez T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 16:10:33