【问题标题】:Hibernate @ManyToMany relationship with composite keys that share a propertyHibernate @ManyToMany 与共享属性的复合键的关系
【发布时间】:2024-05-15 17:40:02
【问题描述】:

所以我正在使用一个使用大量组合键的数据库。我正在尝试使用 JPA/hibernate 为其中一个关系设置 JoinTable。这是正在做的一个精简示例

父类

@Entity
@Table(name = "PROTAGONIST")
public class Protagonist {
    private Integer id;
    private String name;

    @Id
    @Column(name = "id", nullable = false)
    public Integer getId() {return id;}

    public void setId(Integer id) { this.id = id;}

    @Column(name = "pro_name", nullable = false, length = 50)
    public String getName() {return name;}

    public void setName(String name) { this.name = name;}
}

项目 ID 类

@Embeddable
public class ItemId {
    private int pId;
    private short invSlot;

    @Column(name = "P_Id", nullable = false)
    public int getpId() {return pId;}

    public void setpId(int pId) { this.pId = pId;}

    @Column(name = "Inv_SlotNum", nullable = false)
    public short getInvSlot() {return invSlot;}

    public void setInvSlot(short invSlot) { this.invSlot = invSlot;}
}

物品类别

public class Item {
    private ItemId id;
    private String itemName;
    private Double cost;
    private Set<Buff> buffs;

    @EmbeddedId
    public ItemId getId() {return id;}

    public void setId(ItemId id) { this.id = id;}

    @Column(name = "Item_Name", nullable = false, length = 100)
    public String getItemName() {return itemName;}

    public void setItemName(String name) { this.itemName = name;}

    @Column(name = "Item_Cost")
    public Double getCost() {return cost;}

    public void setCost(Double cost) { this.cost = cost;}

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "BUFFSONITEMS",
        joinColumns = {
            @JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
            @JoinColumn(name = "Inv_SlotNum", referencedColumnName = "Inv_SlotNum")
        },
        inverseJoinColumns = {
            @JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
            @JoinColumn(name = "Buff_SlotNum", referencedColumnName = "Buff_SlotNum")
        }
    )
    public Set<Buff> getBuffs() {return buffs;}

    public void setBuffs(Set<Buff> buffs) { this.buffs = buffs;}
}

Buff ID 类

@Embeddable
public class BuffId {
    private Integer pId;
    private Short buffSlotNum;

    @Column(name = "P_Id", nullable = false)
    public Integer getpId() {return pId;}

    public void setpId(Integer pId) { this.pId = pId;}

    @Column(name = "Buff_SlotNum", nullable = false)
    public Short getBuffSlotNum() {return buffSlotNum;}

    public void setBuffSlotNum(Short buffSeqNum) { this.buffSlotNum = buffSeqNum;}
}

增益类

@Entity
@Table(name = "BUFF")
public class Buff {
    private BuffId id;
    private String buffName;
    private Long duration;
    private Set<Item> buffedItems;

    @EmbeddedId
    public BuffId getId() {return id;}

    public void setId(BuffId id) { this.id = id;}

    @Column(name = "Buff_Name", nullable = false, length = 100)
    public String getBuffName() {return buffName;}

    public void setBuffName(String name) { this.buffName = name;}

    @Column(name = "Duration", nullable = false)
    public Long getDuration() {return duration;}

    public void setDuration(Long duration) { this.duration = duration;}

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "BUFFSONITEMS",
        joinColumns = {
            @JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
            @JoinColumn(name = "Buff_SlotNum", referencedColumnName = "Buff_SlotNum")
        },
        inverseJoinColumns = {
            @JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
            @JoinColumn(name = "Inv_SlotNum", referencedColumnName = "Inv_SlotNum")
        }
    )
    public Set<Item> getBuffedItems() {return buffedItems;}

    public void setBuffedItems(Set<Item> buffedItems) { this.buffedItems = buffedItems;}
}

每当我尝试启动 Spring Boot 时,我都会收到以下异常 org.hibernate.MappingException: Repeated column in mapping for collection: com.blankd.composite.key.Item.buffs column: P_Id。 BUFFSONITEMS 表使用所有 3 列作为该表中每一行的主键的一部分。所有三列在各自的表上也有 forgein 键约束。这意味着 P_Id 对 Buff 和 Item 都有一个 forgein 键约束。

我不确定自己做错了什么,因为我需要 P_Id 才能唯一标识每个表中的行。

【问题讨论】:

    标签: java hibernate jpa


    【解决方案1】:

    您必须选择多对多关系的一方作为“拥有”方。然后,“反向”端必须使用 mappedBy 元素。

    如果您选择Item.buffs 作为拥有方,您将像这样映射Buff.buffedItems

        @ManyToMany(mappedBy="buffs")
        public Set<Item> getBuffedItems() {return buffedItems;}
    

    【讨论】:

    • 即使我使用 mappedBy 参数,我仍然会遇到同样的错误。我给出的例子非常简化。还有另一个使用 ManyToOne 关系和 JoinColumns 的对象,它也将 P_Id 映射为要使用的列之一。这会导致问题吗?
    • @dblanken 您可能需要使用“派生身份”。这在JPA spec 2.4.1 节中有描述。
    • 我在该列中没有看到任何与多对多关系相关的内容。我试图避免在我的连接表上使用一对多关系。