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

Spring Boot JPA Hibernate已加@Id仍报实体无标识符错误

问题分析

你遇到的No identifier specified for entity: com.poc.questionnarie.QuestionResponse错误,核心原因是Hibernate 5.2+对复合主键的映射规则有更严格的要求。虽然你在QuestionResponse的两个@ManyToOne字段上加了@Id注解,但这种写法在新版本Hibernate中已经不足以被识别为合法的复合主键配置了。

解决方案

针对你的双向多对多中间实体映射场景,有两种标准的修复方式:

方案1:使用@IdClass注解

这种方式适合直接将关联实体作为主键一部分的场景:

  1. 首先创建复合主键类,必须实现Serializable接口,字段名要和QuestionResponse中的关联字段名一致:
import java.io.Serializable;
import java.util.Objects;

public class QuestionResponseId implements Serializable {
    private Long question; // 对应QuestionResponse中的question字段
    private Long response; // 对应QuestionResponse中的response字段

    public QuestionResponseId() {}

    public QuestionResponseId(Long questionId, Long responseId) {
        this.question = questionId;
        this.response = responseId;
    }

    // 必须重写equals和hashCode以保证主键唯一性校验
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        QuestionResponseId that = (QuestionResponseId) o;
        return Objects.equals(question, that.question) &&
               Objects.equals(response, that.response);
    }

    @Override
    public int hashCode() {
        return Objects.hash(question, response);
    }
}
  1. 修改QuestionResponse类,添加@IdClass注解并显式指定关联列名:
@Entity
@Table(name = "question_response")
@IdClass(QuestionResponseId.class) // 关联复合主键类
public class QuestionResponse {
    @Id
    @ManyToOne
    @JoinColumn(name = "question_id", nullable = false)
    private Question question;

    @Id
    @ManyToOne
    @JoinColumn(name = "response_id", nullable = false)
    private Response response;

    // 构造方法、getter/setter保持不变
}

方案2:使用@EmbeddedId + @MapsId注解

这种方式更清晰地分离主键结构与关联关系,适合复杂复合主键场景:

  1. 创建可嵌入的主键类:
import javax.persistence.Embeddable;
import java.io.Serializable;
import java.util.Objects;

@Embeddable
public class QuestionResponseId implements Serializable {
    private Long questionId;
    private Long responseId;

    public QuestionResponseId() {}

    public QuestionResponseId(Long questionId, Long responseId) {
        this.questionId = questionId;
        this.responseId = responseId;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        QuestionResponseId that = (QuestionResponseId) o;
        return Objects.equals(questionId, that.questionId) &&
               Objects.equals(responseId, that.responseId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(questionId, responseId);
    }

    // getter/setter方法
}
  1. 修改QuestionResponse类,通过@EmbeddedId定义主键并关联实体:
@Entity
@Table(name = "question_response")
public class QuestionResponse {
    @EmbeddedId
    private QuestionResponseId id;

    @ManyToOne
    @MapsId("questionId") // 将主键中的questionId映射到question实体
    @JoinColumn(name = "question_id", nullable = false)
    private Question question;

    @ManyToOne
    @MapsId("responseId") // 将主键中的responseId映射到response实体
    @JoinColumn(name = "response_id", nullable = false)
    private Response response;

    // 构造方法建议传入关联实体并初始化主键
    public QuestionResponse(Question question, Response response) {
        this.question = question;
        this.response = response;
        this.id = new QuestionResponseId(question.getId(), response.getId());
    }

    // 保留空构造方法
    public QuestionResponse() {}

    // getter/setter方法
}

额外修复点

检查你的Response类,发现序列生成器配置错误:

@SequenceGenerator(name = "rid_seq", sequenceName="questions_id_seq")

这里的sequenceName应该是responses_id_seq(对应数据库中responses表的序列),否则会导致Response的ID生成复用Question的序列,引发数据错误。修改为:

@SequenceGenerator(name = "rid_seq", sequenceName="responses_id_seq")

验证与测试

完成上述修改后,重新部署应用,Hibernate就能正确识别QuestionResponse的复合主键,解决"No Identifier error"问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:52:43