首页 > 解决方案 > 单边 OneToMany 复合键也是外键,应该使用@IdClass 还是@Embeddable?

问题描述

我很难决定是使用 @IdClass 还是 @Embeddable 来为如下所示的 OneToMany 数据库关系建模复合主键。

关系表

user_iduser表的主键,并user_merchant同时使用merchant_iduser_id作为其复合键。一个用户可以与多个商家打交道,反之亦然。该user_merchant表跟踪用户和商家之间的关系。

由于 ' user' 是唯一需要了解其关系的人,而不是商家,因此我将其建模为单向的一对多关系。

在用户实体中,我希望像这样建模:

@Entity
@Table(name = "user")
public class User extends Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Long userId;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private Set<Merchant> merchants;

}

对于 Merchant 实体来说,这有点棘手。

public class Merchant implements Serializable{

    @Column
    private String merchantId;

    .....

}

Merchant如果在类中未指定 @Id,则运行这段代码将失败。我不确定在这种情况下是否应该使用@IdClass 或@Embedded/@Embeddable,以及两者相比有什么好处。

我可以避免像大多数解决方案显示的那样在 Merchant 类中引入新的 User 对象吗?由于商家并不真正需要了解用户。

@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "location_id")
private User user;

以及如何User在第一次保存对象时填充此字段?如果可能的话,我认为我不需要在已经是同一用户对象的属性的商家对象中使用另一个用户引用。

提前致谢!

标签: javahibernatejpacomposite-primary-keycomposite-key

解决方案


这是我解决这个问题的方法:

我已将这种关系确定为父对象和子对象之间的单边一对多关系的情况,其中子对象的复合主键部分由父对象的外键组成。

最后,我选择使用@IdClass,因为我仍然需要访问 MercerId,这似乎是最简单的方法。

我在用户类中所做的唯一更改是在 @OneToMany中删除@JoinColumn(name = "user_id")并替换它。mappedBy = "user"如果没有此更改,第一次保留此实体就可以正常工作,但是当我尝试在从集合中删除商家的同时更新此实体时,它给了我一些错误。

@Entity
@Table(name = "user")
public class User extends Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Long userId;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, 
               mappedBy = "user", fetch = FetchType.LAZY)
    private Set<Merchant> merchants;

    @PrePersist
    public void prePersistUser() {
        if (this.merchants != null) {
            this.merchants.forEach(merchant-> {
            merchant.setUser(this);
            });
        }
    }

}

至于商家类,我MerchantPK.class通过@IdClass 链接到 Merchant.java。

我还确保在@JoinColumn 内部insertableupdatable = false因为这种关系是单方面的,并且只能从父方(用户)插入/更新。

@Entity
@IdClass(MerchantPK.class)
@Table(name = "user_merchant")
public class Merchant {

    @Id
    @Column(nullable = false)
    private String merchantId;


    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", insertable = false, updatable = false)
    private UserEntity user;

}

在单边一对多关系的情况下,只需要 1 个 @JoinColumn,在这种情况下,它就是 @ManyToOne 所在的位置。带有@OneToMany 的对象仍然是关系的所有者,即使使用 mappedBy,因为我们指定了insertable = falseand updatable = false


推荐阅读