EntityManager.remove 和 EntityManager.persist 上的 JPA 重复输入错误

JPA duplicate entry error on EntityManager.remove and then EntityManager.persist

我正在使用 JPA 的 Hibernate 实现。假设我有一个对象列表,我必须将其保存在一个名为 Event 的 table 中。所有这些对象都具有相同的邮政编码。

public class Event {
    String id;
    String zipCode;
    String locationCode;
    String eventName;
    String eventDesc;
}

这里的id是主键,zipCodelocationCode一起构成唯一键(UK_zipCode_locationCode)。 table 可能已经有具有给定邮政编码的对象。因此,我没有找到应该添加、删除或更新哪些对象,而是首先删除 table 中具有给定邮政编码的所有对象,然后插入所有给定对象。

// getEventsToAdd method returns the list of events to be added for the zipCode 1234
// getEventsFromTheDB method returns all the events in the db with the zipCode 1234 

List<Event> eventsToAdd = getEventsToAdd("1234");
List<Event> oldEvents = getEventsFromTheDB("1234");

for (Event e : oldEvents) {
    entityManager.remove(e);
}

for (Event e : eventsToAdd) {
    entityManager.persist(e);
}
entityManager.flush();
// ...

这在 oldEvents 列表为空或 oldEvents 中的所有对象也在 eventsToAdd 列表中时起作用(我的意思是具有相同 ID 和相同的邮政编码)。

但是,如果oldEvents中有一些事件对象具有不同的id,即与eventsToAdd列表中的任何对象的id不匹配,则抛出异常

Duplicate Entry found for key UK_zipCode_locationCode

错误就好像旧事件没有从 table 中删除,现在插入具有相同 zipCode 和 locationCode 值的事件导致 org.hibernate.exception.ConstraintViolationException

但是,如果我在删除旧事件后调用 entityManager.flush(),它会起作用 -

// This works!

for (Event e : oldEvents) {
    entityManager.remove(customizedProviderAttribute);
}
// flush after removing all the old events
entityManager.flush();
for (Event e : eventsToAdd) {
    entityManager.persist(e);
}

那么,为什么在最后刷新不起作用,但在删除旧实体后刷新有效?

当您调用 remove 和 persist 时,实体管理器不一定会发出 delete 和 insert 语句,它会等待并稍后生成 SQL,通常是在您显式或隐式刷新时。这意味着语句的顺序将不同,因此某些插入可能会在某些删除之前执行,从而触发约束冲突。在这种情况下,中间冲洗的解决方法是常见的做法。

默认情况下,EntityManager 在提交事务时执行所有 SQL 命令。但是,它可以决定执行 SQL 命令的顺序,在您的情况下,插入是在删除之前完成的,这导致了 ConstraintViolationExceptionflush() 导致所有 SQL 立即完成,因此您实现了先删除再插入。世界并不完美,Hibernate 也不完美。

在您的第二个工作示例中,当您在删除后刷新时,Hibernate 会将这些实体的状态更改为已删除,以便与数据库保持同步,如果删除是物理完成的,并且在您的日志中您会看到delete sql query issued,that's why when you persist those same entities,it'll work as for the first example not working,因为这些实体仍处于管理状态,而你正试图再次坚持它们,这导致重复条目,如@Michal 所说,删除之前发出的插入,因为不能保证顺序。