我的实体同时具有自动生成的主键(id)和业务键(命名空间)。我需要通过替换旧记录来更新记录。因此,我通过业务密钥搜索它,将其删除并保存一个新实体。如果每个操作都在自己的事务中,则此方法有效。但是,一旦我将它们全部放入同一个事务中,当执行 save() 时,delete() 尚未执行,因此我遇到了约束违规。
transactionTemplate.execute(status -> {
MyEntity oldEntity = repository.findByNamespace(namespace);
if (oldEntity != null) {
repository.delete(oldEntity);
}
repository.save(newEntity);
return null;
});
我实际上设法通过添加来绕过它
repository.flush();
但我真的不明白为什么我需要这个flush()。
因为repository.flush()通过调用EntityManager.flush()将更改刷新到数据库。因此,当您在delete()之后刷新更改时,sql将被执行,并且以下保存将不会有问题。
如果您不调用刷新,则由持久性提供者来确定何时以事务提交时间为截止日期来刷新更改。此外,提供程序不会以任何特定顺序刷新更改,因此有时您的操作可能会成功,有时则不会。通常,提供程序会等到提交时间刷新,但您可以通过设置刷新模式来影响它:
for entitymanager
EntityManager.setFlushMode(FlushModeType type);
or for query
Query.setFlushMode(FlushModeType type);
Spring data JPA 中也有一个等效的设置,我确信,但我不完全知道它是哪一个。
但请注意,立即刷新更改会降低性能,因此使用时应小心。在您的特定情况下,最好更新实体,然后删除它,然后使用相同的业务密钥保留新实体。
我遇到了同样的事情,我需要先删除对象,然后创建一个新对象,但遇到约束冲突异常,因为删除未刷新到数据库并且插入失败。
我尝试了各种方法和
delete*
方法的变体,但只有deleteAllInBatch(Iterable<T> entity)
有效。