繁体   English   中英

Hibernate 从 5 迁移到 5.5 后,将实体添加到集合会导致 ConstraintViolationException

[英]After Hibernate migration from 5 to 5.5 adding entities to collections causes ConstraintViolationException

我们有这样的代码(我简化了代码以使其更清晰):

@Entity
@Table(name="storages")
class Storage {
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinTable(name = "storage_items", joinColumns = @JoinColumn(name = "storage_id"), inverseJoinColumns = @JoinColumn(name = "item_id"))
    private Set<Item> items;

    void putItemToStorage(Session session) {
        Item item = new Item();
        item.setStorage(this);
        session.save(item);
        items.add(item);
    }

}

@Entity
@Table(name="items")
class Item {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "storage_id")
    private Storage storage;

    public void setStorage(Storage storage) {
        this.storage = storage;
    }
}

我们在事务中调用“putItemToStorage”,但在 hibernate 5.5 中它会导致以下错误,而在 hibernate 5 中,相同的代码就像一个魅力:

> javax.persistence.PersistenceException:
> org.hibernate.exception.ConstraintViolationException: could not execute statement
> ...
> caused by org.postgresql.util.PSQLException: ERROR: insert or update on table
> "storage_items" violates foreign key constraint
> "fkla3c4upmtw4myssb3bfg2svkj"   Detail: Key (storage_id)=(164) is not
> present in table "items".

因此,hibernate 5 解决了对items表和storage_items表的插入并按预期工作(将这两个项目添加到items表并通过加入表storage_items项目链接到相应的存储),但在 hibernate 5.5 中它不再起作用。 我在谷歌和文档上花了很多时间,但找不到更改的内容或我做错了什么。

我在其他地方遇到了类似的错误,我暂时解决了它,将对象的保存分开并将对象插入到 2 个单独的事务中(它有效,但绝对不是正确的解决方案),因此,如何正确修复它的任何帮助都是非常感谢。

您应该只在一侧(持有关系的一侧)定义映射,例如。 像这样:

@Entity
@Table(name="storages")
class Storage {

    @OneToMany(mappedBy = "storage", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Set<Item> items;

    void putItemToStorage() {
        Item item = new Item();
        item.setStorage(this);
        items.add(item);
    }

}

@Entity
@Table(name="items")
class Item {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "storage_id", referencedColumnName = "id")
    private Storage storage;

    public void setStorage(Storage storage) {
        this.storage = storage;
    }
}

请注意,这种方法很可能会导致 2 个查询:

  1. 这将在 DB 中创建带有storage_id = null Item记录
  2. 这将更新记录并将storage_id设置为它应该是的值

为了防止它调整字段storage上的注释:

@Entity
@Table(name="items")
class Item {

    @ManyToOne(optional = false)
    @JoinColumn(name = "storage_id", referencedColumnName = "id", nullable = false, updatable = false)
    private Storage storage;

}

您还可以考虑将orphanRemoval = true添加到items ,以防您想删除数据库中的记录。

@Entity
@Table(name="storages")
class Storage {

    @OneToMany(mappedBy = "storage", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
    private Set<Item> items;

}

您的代码应该如下所示:

// assumming within transaction context
var storage = // get from eg. EntityManager or JPA repository (in spring)
storage.putItemToStorage();
// There is no need to call EntityManager::persist or EntityManager::merge
// if you are withing transaction context 
// and you are working with managed entity
// and have cascade = CascadeType.ALL

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM