JPA中外键兼主键问题:Product关联Document双外键主键识别异常
解决JPA中Product实体双外键作为复合主键的识别问题
我来帮你搞定这个问题——你遇到的情况是Eclipse的JPA内容图没把那两个Document外键识别成Product的主键,大概率是因为复合主键的配置细节没做对。下面给你两种标准的实现方案,选一种适配你的代码就行:
先确认Document实体的基础配置
首先得保证你的Document和它的主键类DocumentId是合规的,这是后续配置的基础:
Document主键类(DocumentId.java)
import java.io.Serializable; import java.util.Objects; public class DocumentId implements Serializable { // 这里的字段要和Document实体的@Id字段完全对应,比如Document用Long类型的id作为主键 private Long id; // 必须有无参构造器 public DocumentId() {} public DocumentId(Long id) { this.id = id; } // 必须重写equals和hashCode,JPA依赖这个判断主键唯一性 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DocumentId that = (DocumentId) o; return Objects.equals(id, that.id); } @Override public int hashCode() { return Objects.hash(id); } // getter和setter方法 public Long getId() { return id; } public void setId(Long id) { this.id = id; } }
Document实体类
import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.Table; @Table(name = "DOCUMENT") @Entity @IdClass(DocumentId.class) public class Document { @Id private Long id; // 你的其他业务属性... // getter和setter方法 public Long getId() { return id; } public void setId(Long id) { this.id = id; } }
方案一:用@IdClass实现Product的复合主键
这种方式适合想把主键字段和实体关联分开定义的场景:
Product主键类(ProductId.java)
import java.io.Serializable; import java.util.Objects; public class ProductId implements Serializable { // 字段名要遵循「关联属性名+Document主键字段名」的规则,比如关联属性是doc1,Document主键是id,这里就叫doc1Id private Long doc1Id; private Long doc2Id; public ProductId() {} public ProductId(Long doc1Id, Long doc2Id) { this.doc1Id = doc1Id; this.doc2Id = doc2Id; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ProductId productId = (ProductId) o; return Objects.equals(doc1Id, productId.doc1Id) && Objects.equals(doc2Id, productId.doc2Id); } @Override public int hashCode() { return Objects.hash(doc1Id, doc2Id); } // getter和setter方法 public Long getDoc1Id() { return doc1Id; } public void setDoc1Id(Long doc1Id) { this.doc1Id = doc1Id; } public Long getDoc2Id() { return doc2Id; } public void setDoc2Id(Long doc2Id) { this.doc2Id = doc2Id; } }
Product实体类
import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Table(name = "PRODUCT") @Entity @IdClass(ProductId.class) public class Product { // 关键:同时用@Id和@ManyToOne,把这个关联字段标记为主键+外键 @Id @ManyToOne @JoinColumn(name = "DOC1_ID", referencedColumnName = "ID") private Document doc1; @Id @ManyToOne @JoinColumn(name = "DOC2_ID", referencedColumnName = "ID") private Document doc2; // 你的其他业务属性... // getter和setter方法 public Document getDoc1() { return doc1; } public void setDoc1(Document doc1) { this.doc1 = doc1; } public Document getDoc2() { return doc2; } public void setDoc2(Document doc2) { this.doc2 = doc2; } }
方案二:用@EmbeddedId实现Product的复合主键
这种方式更直观,把复合主键做成一个可嵌入的组件,适合逻辑上把两个外键当成一个整体主键的场景:
Product嵌入主键类(ProductEmbeddedId.java)
import java.io.Serializable; import java.util.Objects; import javax.persistence.Embeddable; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; @Embeddable public class ProductEmbeddedId implements Serializable { @ManyToOne @JoinColumn(name = "DOC1_ID", referencedColumnName = "ID") private Document doc1; @ManyToOne @JoinColumn(name = "DOC2_ID", referencedColumnName = "ID") private Document doc2; public ProductEmbeddedId() {} public ProductEmbeddedId(Document doc1, Document doc2) { this.doc1 = doc1; this.doc2 = doc2; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ProductEmbeddedId that = (ProductEmbeddedId) o; return Objects.equals(doc1, that.doc1) && Objects.equals(doc2, that.doc2); } @Override public int hashCode() { return Objects.hash(doc1, doc2); } // getter和setter方法 public Document getDoc1() { return doc1; } public void setDoc1(Document doc1) { this.doc1 = doc1; } public Document getDoc2() { return doc2; } public void setDoc2(Document doc2) { this.doc2 = doc2; } }
Product实体类
import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.Table; @Table(name = "PRODUCT") @Entity public class Product { @EmbeddedId private ProductEmbeddedId id; // 你的其他业务属性... // getter和setter方法 public ProductEmbeddedId getId() { return id; } public void setId(ProductEmbeddedId id) { this.id = id; } }
最后一步:让Eclipse识别配置
配置完之后,右键你的JPA项目,选择JPA -> Synchronize JPA Entities,刷新一下JPA内容图,这时候那两个外键应该就能被正确识别成Product的主键了。
内容的提问来源于stack exchange,提问作者Wesley Rosa
相关产品推荐
相关产品推荐

