【问题标题】:Prevent duplicate entries in a join table in a many-to-many relationship in JPA在 JPA 中以多对多关系防止连接表中的重复条目
【发布时间】:2014-06-13 22:27:08
【问题描述】:

我正在使用 EclipseLink 2.5.1(和 Hibernate 4.3.5 final)。给定 MySQL 中的以下表。

  • 产品
  • prod_colour(连接表)
  • 颜色

产品与其颜色之间存在多对多的关系。

一种产品可以有多种颜色,而一种颜色又可以与多种产品相关联。这种关系在数据库中由这些表表示。

prod_colour 表有两个引用列 prod_idcolour_id 分别来自其相关的父表 productcolour

很明显,实体类Product 有一个颜色列表——java.util.List<Colour>,它被命名为colourList

实体类 Colour 有一个产品列表 - java.util.List<Product>,名为 productList


Colour实体中的关系:

public class Colour implements Serializable {

    @JoinTable(name = "prod_colour", joinColumns = {
        @JoinColumn(name = "colour_id", referencedColumnName = "prod_id")}, inverseJoinColumns = {
        @JoinColumn(name = "prod_id", referencedColumnName = "colour_id")})
    @ManyToMany(mappedBy = "colourList", fetch = FetchType.LAZY)
    private List<Product> productList; //Getter and setter.

    //---Utility methods---

    //Add rows to the prod_colour table.
    public void addToProduct(Product product) {
        this.getProductList().add(product);
        product.getColourList().add(this);
    }

    //Delete rows from the prod_colour table.
    public void removeFromProduct(Product product) {
        this.getProductList().remove(product);
        product.getColourList().remove(this);
    }
}

Product 实体中的关系:

public class Product implements Serializable {

    @JoinTable(name = "prod_colour", joinColumns = {
        @JoinColumn(name = "prod_id", referencedColumnName = "prod_id")}, inverseJoinColumns = {
        @JoinColumn(name = "colour_id", referencedColumnName = "colour_id")})
    @ManyToMany(fetch = FetchType.LAZY)
    private List<Colour> colourList; //Getter and setter.
}

从关联的 EJB 中,插入操作执行如下。

@Override
@SuppressWarnings("unchecked")
public boolean insert(List<Colour> colours, Product product)
{
    int i=0;
    Long prodId=product.getProdId();
    for(Colour colour:colours)
    {
        Product p = entityManager.getReference(Product.class, prodId);
        colour.addToProduct(p);

        if(++i%49==0)
        {
            entityManager.flush();
        }
        entityManager.merge(colour);
        entityManager.merge(p);
    }
    return true;
}

一切正常。


当尝试重复行时(相同的Colour 实体与相同的Product 实体相关联),它们也会被插入到prod_colour 表中,我认为这不会发生。

我是否需要执行一些额外的条件检查以避免重复插入,或者 EclipseLink/Hibernate 有一些机制来防止在这种情况下出现重复?

【问题讨论】:

    标签: hibernate jpa many-to-many eclipselink jpa-2.1


    【解决方案1】:

    这也让我感到惊讶。我一直认为它将连接表中的引用列作为复合主键,但事实并非如此。如果您想拥有唯一的记录,请尝试使用Set 而不是List 或使用复合主键(color_id,product_id)创建您自己的多对多关系表。我没有更好的主意。

    【讨论】:

    • 谢谢。早些时候我使用java.util.Sets,但我用java.util.Lists 替换了它们,因为Sets 有自己的局限性,我在某个地方遇到过。如果使用Sets,是的,它确实会插入重复的行,就像在这种情况下发生的那样。很多时候不建议使用复合主键。
    • 为什么不推荐这样做?我更喜欢在多对多关系中使用我自己的关系表。为什么?如果您的主管告诉您“我想知道该颜色的产品数量”,您将这些信息放在哪里? product_colour 表是最好的地方。当您定义自己的关系表时,您拥有更多的控制权。
    • 我已更改此关系以使用java.util.Set 来防止重复插入prod_colour 表。
    【解决方案2】:

    您可以在 JoinTable 上指定一个 uniqueConstraints,如下所示:

    @JoinTable(name = "prod_colour", joinColumns = {
            @JoinColumn(name = "prod_id", referencedColumnName = "prod_id")}, inverseJoinColumns = {
            @JoinColumn(name = "colour_id", referencedColumnName = "colour_id")}, uniqueConstraints = @UniqueConstraint(columnNames = {
                                "colour_id", "prod_id" }))
    

    当直接对表和通过您的应用程序代码执行直接 sql 时,这将防止条目重复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-26
      • 1970-01-01
      • 1970-01-01
      • 2021-05-10
      相关资源
      最近更新 更多