Spring Boot JPA Hibernate已加@Id仍报实体无标识符错误
问题分析
你遇到的No identifier specified for entity: com.poc.questionnarie.QuestionResponse错误,核心原因是Hibernate 5.2+对复合主键的映射规则有更严格的要求。虽然你在QuestionResponse的两个@ManyToOne字段上加了@Id注解,但这种写法在新版本Hibernate中已经不足以被识别为合法的复合主键配置了。
解决方案
针对你的双向多对多中间实体映射场景,有两种标准的修复方式:
方案1:使用@IdClass注解
这种方式适合直接将关联实体作为主键一部分的场景:
- 首先创建复合主键类,必须实现
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); } }
- 修改
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注解
这种方式更清晰地分离主键结构与关联关系,适合复杂复合主键场景:
- 创建可嵌入的主键类:
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方法 }
- 修改
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
相关产品推荐
相关产品推荐

