我实际上从未完全了解hibernate状态下的这种行为。我在名为“ Parent”的实体中使用@OneToMany关系,其注释如下:
@OneToMany(cascade = {CascadeType.ALL, CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, orphanRemoval = true) @JoinColumn(name = "entity_id", insertable = true, updatable = true, nullable = false) private List<Child> children;
现在,我想在一个事务中执行以下操作:
因此,基本上我只是完全替换其中一个孩子。
据我了解的这个问题,我应该能够做这样的事情:(请注意,这只是一些Java伪代码来说明问题)
@TransactionAttribute(TransactionAttributeType.REQUIRED) public void deleteAndAdd(Long parentId, Long childId) { Parent parent = entityManager.find(parentId); for (Iterator it = parent.children.iterator(); it.hasNext();) { Child child = it.next(); if (child.id == childId) { it.remove(); } } Child newChild = new Child(); parent.children.add(newChild); }
但是,如果新的Child具有与旧的相同的唯一键值,则此操作将失败。因此,在持久保留新的子实体之前,基本上似乎没有正确删除旧的子实体。
如果我在删除旧子项和保留新子项之间添加EntityManager.flush(),如下所示:
@TransactionAttribute(TransactionAttributeType.REQUIRED) public void deleteAndAdd(Long parentId, Long childId) { Parent parent = entityManager.find(parentId); for (Iterator it = parent.children.iterator(); it.hasNext();) { Child child = it.next(); if (child.id == childId) { it.remove(); } } entityManager.flush(); Child newChild = new Child(); parent.children.add(newChild); }
一切正常。在插入新的子对象之前,应先删除该子对象。
因为我不想假设hibernate混合了发送到数据库的语句的顺序,所以我对hibernate有其他假设,事实并非如此。有任何想法为何后一个示例有效,而第一个示例无效吗?
hibernate版本是3.5。数据库是Mysql InnoDB
Hibernate既不了解也不尊重所有数据库约束(例如MySQL唯一约束)。这是他们不打算在短期内解决的已知问题。
对于刷新期间的操作方式,Hibernate具有已定义的顺序。
实体删除总是在插入后发生。我知道的唯一答案是删除约束或添加其他刷新。
编辑:顺便说一句,定义顺序的原因是,这是确保不违反外键约束(他们确实在意的约束之一)的唯一方法,即使用户做错了事。