Postgres分区表复合主键外键下Spring Boot双向一对多关联问题咨询
复合主键关联场景下临时解决方案的合理性分析
临时方案的利弊
- 从功能角度说,这个临时办法能快速搞定
a_fk为空的问题,让业务能正常跑起来,在赶上线或者高负载应急的场景下,算是能凑合用的临时对策。 - 但从代码维护和规范来看,这个方案隐患不小:
- 多了个冗余的
aFk字段,和通过关联实体A获取主键的逻辑重复,万一后续修改A的关联但忘了同步更新aFk,很容易出现数据不一致。 - 违背了JPA关联映射的设计初衷,本该由Hibernate自动维护的外键关系改成手动赋值,以后要是换ORM框架或者调整映射规则,坑会更多。
- 多了个冗余的
更靠谱的修复思路
要彻底解决问题,还是得回到JPA映射的正确配置上,排查之前调@MapsId和CascadeType时出的错:
- 先确认嵌入式主键类
PK有没有正确实现Serializable,并且重写equals()和hashCode()——这是JPA复合主键的硬性要求,之前的错误大概率和这个有关。 - 针对
B实体的复合外键关联,正确的@MapsId用法应该是这样:- 在
B的嵌入式主键里,要包含对应A主键的字段(比如叫aId)。 - 在
B关联A的字段上加上@MapsId("aId"),同时用@JoinColumn指定字段名为a_fk,并设nullable = false。
- 在
- 检查
CascadeType的配置:如果是通过A来保存B,得在A的@OneToMany字段上加上CascadeType.PERSIST或者CascadeType.ALL,同时B的关联字段要设置mappedBy。
修正后的代码示例
举个B实体和对应主键类的正确写法:
@Entity @Table(name = "b") public class B { @EmbeddedId private BPK id; @ManyToOne(fetch = FetchType.LAZY) @MapsId("aId") // 对应BPK里属于A主键的字段名 @JoinColumn(name = "a_fk", nullable = false) private A a; // 其他字段、getter和setter public void setA(A a) { this.a = a; // 不用手动赋值aFk,Hibernate会自动同步id里的aId字段 } } // B的嵌入式主键类 @Embeddable public class BPK implements Serializable { private Long aId; // 和A的主键字段类型保持一致 private Long partitionKey; // B自身的分区键字段 // 必须重写equals和hashCode @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BPK bpk = (BPK) o; return Objects.equals(aId, bpk.aId) && Objects.equals(partitionKey, bpk.partitionKey); } @Override public int hashCode() { return Objects.hash(aId, partitionKey); } }
内容的提问来源于stack exchange,提问作者Corina
相关产品推荐
相关产品推荐

