【问题标题】:After Hibernate migration from 5 to 5.5 adding entities to collections causes ConstraintViolationException在 Hibernate 从 5 迁移到 5.5 后,将实体添加到集合会导致 ConstraintViolationException
【发布时间】:2021-09-20 23:33:53
【问题描述】:

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

@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 表并按预期工作(将 item 添加到 items 表并通过加入表 storage_itemsitem 链接到相应的 storage),但在休眠 5.5 中它不再起作用。我在 google 和文档中花费了相当长的时间,但找不到更改的内容或我做错了什么。

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

【问题讨论】:

    标签: java hibernate orm migration entity


    【解决方案1】:

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

    @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. 这将在数据库中创建Item 记录storage_id = null
    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
    

    【讨论】:

    • 谢谢,我会尝试您的建议,如果有帮助,我会回复您的信息
    猜你喜欢
    • 1970-01-01
    • 2011-01-05
    • 2015-12-18
    • 1970-01-01
    • 2022-12-12
    • 1970-01-01
    • 1970-01-01
    • 2015-07-23
    • 2013-08-08
    相关资源
    最近更新 更多