Предотвращение дублирования записей в таблице соединений в отношениях «многие ко многим» в JPA

Я использую EclipseLink 2.5.1 (и Hibernate 4.3.5 final). Учитывая следующие таблицы в MySQL.

  • товар
  • prod_color (присоединить таблицу)
  • цвет

Между продуктами и их цветами существует связь «многие ко многим».

Продукт может иметь много цветов, а цвет, в свою очередь, может быть связан со многими продуктами. Эта связь выражается в базе данных этими таблицами.

Таблица prod_colour имеет два ссылочных столбца prod_id и colour_id из связанных родительских таблиц product и colour соответственно.

Как очевидно, класс объектов 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 есть какой-то механизм для предотвращения дублирования в таких ситуациях?


person Tiny    schedule 28.04.2014    source источник


Ответы (2)


Для меня это тоже удивительно. Я всегда думал, что он делает ссылочные столбцы в таблице соединений составным первичным ключом, но это не так. Если вы хотите иметь уникальные записи, попробуйте использовать Set вместо List или создайте собственную таблицу отношений ManyToMany с составным первичным ключом (color_id, product_id). У меня нет лучшей идеи.

person Peter Wroblewski    schedule 29.04.2014
comment
Спасибо. Раньше я использовал java.util.Set, но заменил их на java.util.List, потому что у Set есть свои ограничения, с которыми я где-то сталкивался. В случае использования Sets да, он вставляет повторяющиеся строки, как это происходит в этом случае. Использование составного первичного ключа часто не рекомендуется. - person Tiny; 29.04.2014
comment
Почему это не рекомендуется? Я предпочитаю использовать свою собственную таблицу отношений во многих отношениях. Почему? Если ваш начальник скажет вам: «Я хочу узнать количество товаров этого цвета», куда вы поместите эту информацию? Таблица product_color — лучшее место. И у вас гораздо больше контроля, когда вы определяете собственную таблицу отношений. - person Peter Wroblewski; 29.04.2014
comment
Я изменил это отношение, чтобы использовать java.util.Set, чтобы предотвратить повторную вставку в таблицу prod_colour. - person Tiny; 10.06.2014

Вы можете указать uniqueConstraints в JoinTable, как показано ниже:

@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 выполняется непосредственно для таблицы и через код вашего приложения.

person Oladipupo Aina    schedule 10.11.2019