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

使用@EmbeddedID复合键的JPA仓库嵌套属性查询返回空值问题

问题描述

在基于@EmbeddedID实现复合键的JPA实体场景中,调用JPA仓库的嵌套属性查询方法(findByMyPk_Entity1_Id)返回的列表元素数量正确,但所有元素为空;而调用findAll方法却能正常获取完整实体列表。


相关代码

MySQL视图

CREATE OR REPLACE VIEW  my_view  
AS  
SELECT      
    col1,     
    col2,     
    col3,     
    col4,     
    col5,     
    col6 
FROM     
    my_table

JPA实体类

@Entity
@Table(name = "my_view")
@Getter
@Setter
public class MyEntity {

    @EmbeddedId
    private MyPk myPk;
    
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "col1", insertable = false, updatable = false)
    private Entity1 entity1 ;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col2", insertable = false, updatable = false)
    private Entity2 entity2;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col3", insertable = false, updatable = false)
    private Entity3 entity3;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col4", insertable = false, updatable = false)
    private Entity4 entity4;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col5", insertable = false, updatable = false)
    private Entity5 entity5;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col6", insertable = false, updatable = false)
    private Entity6 entity6;

}

嵌入式ID类

@NoArgsConstructor
@EqualsAndHashCode
@AllArgsConstructor
@Embeddable
public class MyPk implements Serializable {

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "col1", insertable = false, updatable = false)
    private Entity1 entity1 ;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col2", insertable = false, updatable = false)
    private Entity2 entity2;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col3", insertable = false, updatable = false)
    private Entity3 entity3;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col4", insertable = false, updatable = false)
    private Entity4 entity4;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col5", insertable = false, updatable = false)
    private Entity5 entity5;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col6", insertable = false, updatable = false)
    private Entity6 entity6;

}

JPA仓库接口

public interface MyEntityRepository extends JpaRepository<MyEntity, MyPk> {

    List<MyEntity> findByMyPk_Entity1_Id(Long id);

}

补充信息

  • Entity1包含id属性
  • 尝试过@IdClass等复合键方案,均出现运行时错误
  • 当前方案无运行时错误,但调用findByMyPk_Entity1_Id返回空元素,仅数量正确
  • 无法使用序列或唯一列方案,尝试@MapIds也返回空元素
  • 使用findAll方法可在调试器中获取完整实体列表
  • 环境版本:spring-data-jpa:1.9.4、hibernate-jpa-2.1-api:1.0.0、Java 8(遗留项目)

问题原因及解决方案

核心原因

嵌入式ID(@Embeddable)类中使用@ManyToOne关联实体违反JPA规范——复合主键必须由基本数据类型(如Long、String)或可序列化的简单类型组成,不能直接关联实体。这种错误映射会导致Spring Data JPA查询时,无法正确将数据库字段值映射到实体的复合主键及关联属性,最终出现元素数量正确但内容为空的情况。

修复步骤

  1. 重构嵌入式ID类:将MyPk中的实体关联替换为对应视图列的基本类型字段:
@NoArgsConstructor
@EqualsAndHashCode
@AllArgsConstructor
@Embeddable
public class MyPk implements Serializable {

    private Long col1;
    private Long col2;
    private Long col3;
    private Long col4;
    private Long col5;
    private Long col6;

}

注:字段类型需与数据库中col1-col6的实际类型匹配,比如字符串类型用String。

  1. 调整实体类关联映射:保留MyEntity中的@ManyToOne关联,明确referencedColumnName对应关联实体的主键:
@Entity
@Table(name = "my_view")
@Getter
@Setter
public class MyEntity {

    @EmbeddedId
    private MyPk myPk;
    
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "col1", insertable = false, updatable = false, referencedColumnName = "id")
    private Entity1 entity1 ;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "col2", insertable = false, updatable = false, referencedColumnName = "id")
    private Entity2 entity2;
    
    // 其余关联字段按相同规则调整
}
  1. 修改仓库查询方法:基于主键的基本类型字段编写查询,或用@Query自定义更清晰的查询逻辑:
public interface MyEntityRepository extends JpaRepository<MyEntity, MyPk> {

    // 方式一:基于主键字段的嵌套查询
    List<MyEntity> findByMyPk_Col1(Long id);

    // 方式二:自定义JPQL查询
    @Query("SELECT e FROM MyEntity e WHERE e.entity1.id = :id")
    List<MyEntity> findByEntity1Id(@Param("id") Long id);
}

额外说明

  • 原方案中findAll能正常工作,是因为Hibernate加载全量数据时会直接映射结果集到实体所有字段;而嵌套属性查询时,Spring Data JPA会尝试通过复合主键的关联实体构建逻辑,因主键映射错误导致无法填充数据。
  • 对于低版本Spring Data JPA(1.9.x)的遗留项目,严格遵循JPA规范的映射方式能避免绝大多数异常映射问题。

内容的提问来源于stack exchange,提问作者amvik13

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 13:35:18