复合主键ManyToMany映射出现重复列映射异常求助
错误信息
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Repeated column in mapping for collection: com.abc.reports.entities.Packages.protectedItems column: account_id
相关代码
复合主键类
@Embeddable @Getter @Setter @ToString public class PackageId implements Serializable { private String accountId; private Long packageId; } @Embeddable @Getter @Setter @ToString public class ProtectedItemsId implements Serializable { private String accountId; private Long protectedItemId; }
实体类
Packages.class
@Entity @Getter @Setter @ToString public class Packages { @EmbeddedId private PackageId packageId; private String packageName; @ManyToMany(mappedBy = "packageItemsList") @JsonIgnore private List<ProtectedItems> protectedItems; }
ProtectedItems.class
@Entity @Getter @Setter public class ProtectedItems implements Serializable { @EmbeddedId private ProtectedItemsId protectedItemId; private String protectedItemName; @ManyToMany @JoinTable( name="jt_packages_protected_items", joinColumns = {@JoinColumn(name="accountId", insertable=false, updatable=false), @JoinColumn(name="protectedItemId", insertable=false, updatable=false)}, inverseJoinColumns = {@JoinColumn(name="accountId", insertable=false, updatable=false), @JoinColumn(name="packageId", insertable=false, updatable=false)} ) private List<Packages> packageItemsList; @ManyToMany(mappedBy = "protectedItemsList") @JsonIgnore private List<StreamMappings> streamMappings; @Override public String toString() { return "ProtectedItems{" + "protectedItemId=" + protectedItemId + ", protectedItemName='" + protectedItemName + ", packageItemsList=" + packageItemsList.hashCode() + ", streamMappings=" + streamMappings + '}'; } }
问题原因与解决方法
核心原因
错误根源在于ProtectedItems类的@JoinTable注解中,joinColumns和inverseJoinColumns同时映射了accountId列,导致Hibernate认为关联表jt_packages_protected_items中的account_id被重复绑定,触发重复列映射异常。
由于两个实体的复合主键都包含accountId,关联表中该字段是共享的,只需在一侧映射即可关联两边的账户维度。
修复步骤
- 修改
ProtectedItems中的@JoinTable配置,移除inverseJoinColumns中的accountId映射:
@ManyToMany @JoinTable( name="jt_packages_protected_items", joinColumns = { @JoinColumn(name="accountId", referencedColumnName="accountId", insertable=false, updatable=false), @JoinColumn(name="protectedItemId", referencedColumnName="protectedItemId", insertable=false, updatable=false) }, inverseJoinColumns = { @JoinColumn(name="packageId", referencedColumnName="packageId", insertable=false, updatable=false) } ) private List<Packages> packageItemsList;
- 补充复合主键类的
equals()和hashCode()方法(Hibernate处理复合主键的必要条件,避免潜在的实体识别问题):
以PackageId为例:
@Embeddable @Getter @Setter @ToString public class PackageId implements Serializable { private String accountId; private Long packageId; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PackageId packageId1 = (PackageId) o; return Objects.equals(accountId, packageId1.accountId) && Objects.equals(packageId, packageId1.packageId); } @Override public int hashCode() { return Objects.hash(accountId, packageId); } }
同理给ProtectedItemsId添加相同逻辑的equals()和hashCode()方法。
说明
关联表jt_packages_protected_items的结构应包含三个字段:accountId、protectedItemId、packageId,通过accountId关联两个实体的账户维度,protectedItemId和packageId分别关联对应实体的主键字段,这样既保证了关联关系的正确性,又避免了重复列映射的问题。
内容的提问来源于stack exchange,提问作者cbot

