Spring JpaRepository:在同一事务中使用delete()和save()方法

25

我的实体同时具有自动生成的主键(id)和业务关键字(namespace)。我需要通过替换旧记录来更新该记录。因此,我通过业务关键字进行搜索,删除它并保存一个新实体。如果每个操作都在自己的事务中,则可以正常工作。但是,一旦我将它们全部放入同一个事务中,在执行save()时,delete()尚未执行,因此我会遇到约束违规错误。

transactionTemplate.execute(status -> {
    MyEntity oldEntity = repository.findByNamespace(namespace);
    if (oldEntity != null) {
        repository.delete(oldEntity);
    }
    repository.save(newEntity);
    return null;
});

我实际上通过添加成功绕过了它

repository.flush();

但我真的不明白为什么我需要这个flush()。


1
我认为这是因为JPA提供程序可以自由地重新组织和/或优化持久上下文中待处理更改的数据库写入,特别是JPA提供程序不感觉有必要按照持久上下文的个别更改所涉及的顺序和形式执行数据库写入。无论如何,所描述的情况是已知的,并且flush()是已知的解决方法。 - Michal
当我执行flush()并尝试保存时,它会抛出JpaObjectRetrievalFailureException异常。 - Sri
2个回答

17

因为repository.flush()通过调用EntityManager.flush()将更改刷新到数据库,所以在delete()之后刷新更改会执行sql并且接下来的save操作不会出现问题。

如果您不调用flush,则由持久性提供程序决定何时在事务提交时间之前刷新更改。此外,提供程序不按任何特定顺序刷新更改,因此有时可能会成功执行操作,有时则不会。通常,提供程序等待提交时间刷新,但您可以通过设置刷新模式来影响它:

for entitymanager
EntityManager.setFlushMode(FlushModeType type);

or for query
Query.setFlushMode(FlushModeType type);

我相信在Spring Data JPA中也有类似的设置,但我不确定具体是哪一个。

需要注意的是,立即刷新更改会降低性能,因此在使用时应谨慎。在您的特定情况下,更新实体比删除实体然后使用相同的业务键持久化新实体更好。


答案确实解释了问题,但并不完全适用于提供的代码示例。 - Pwnstar

0
我遇到了同样的问题,我需要先删除对象,然后再创建一个新的对象,但是由于删除操作没有刷新到数据库,插入操作失败了,导致出现了约束违规异常。
我尝试了各种方法和不同的delete*方法的变体,但只有deleteInBtraiatch(Iterable<T> entity)起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接