如何在Hibernate中结合@EmbeddedId使用@OneToMany关联映射
解决Hibernate复合键@OneToMany关联的重复列映射问题
我来帮你排查这个问题,你遇到的repeated column in mapping for entity AddressHistory错误,核心原因是复合主键类里重复映射了同一个数据库列,咱们一步步来修复:
错误原因分析
你在AddressKey这个可嵌入主键类里,同时做了两个操作:
- 用
@Column(name = "id")映射了数据库的id列 - 又用
@ManyToOne+@JoinColumn(name="id")再次映射了同一个id列
Hibernate无法处理这种重复的列映射,所以抛出了错误。而且按照Hibernate的规范,可嵌入的复合主键类(@Embeddable)应该只包含基础数据类型/可嵌入类型,不应该包含实体关联(@ManyToOne),关联关系应该放到实体类本身中。
修复方案及代码调整
1. 修正复合主键类AddressKey
移除里面的Person关联字段,只保留复合主键对应的数据库字段:
@Embeddable @Getter @Setter public class AddressKey implements Serializable { private static final long serialVersionUID = -307823488229761699L; @Column(name = "id") private Long id; @Column(name = "city") private Long city; @Column(name = "locale") private String locale; @Column(name = "type") private String type; }
2. 调整AddressHistory实体类
把Person的关联关系移到实体中,并用@MapsId注解关联复合主键的id字段,避免重复映射:
@Entity @Table(name = "address", schema = "test") @Setter @Getter public class AddressHistory { @EmbeddedId private AddressKey key; // 用@MapsId指定关联的Person.id对应复合主键中的id字段 @ManyToOne @MapsId("id") @JoinColumn(name = "id") private Person person; @Column(name = "active") private boolean active; @Column(name = "current") private boolean current; }
3. 修正Person实体的关联映射
把@OneToMany的mappedBy指向AddressHistory中的person属性(而不是之前的key.person):
@Entity @Table(name = "person", schema="test") @ToString @Getter @Setter // 补充Getter/Setter,保证属性可访问 public class Person { @Id @Column(name = "id") private Long id; @OneToMany(mappedBy="person", fetch=FetchType.EAGER, cascade=CascadeType.ALL) private Set<AddressHistory> addressHistory; }
关键说明
@MapsId的作用是将关联实体(Person)的主键值,自动填充到当前实体复合主键(AddressKey)的指定字段(id)中,既保证了关联关系,又避免了重复列映射。- 复合主键类必须实现
Serializable接口(你已经做到了),这是Hibernate对复合主键的强制要求。 - 给
Person补充了@Getter和@Setter,避免因为无法访问addressHistory导致关联失效。
内容的提问来源于stack exchange,提问作者keepmoving
相关产品推荐
相关产品推荐

