【问题标题】:Hibernate removing child entities休眠删除子实体
【发布时间】:2026-02-06 09:00:02
【问题描述】:

我有一个带有 OneToMany 列表的实体,定义如下:

   @OneToMany(
         mappedBy = "fieldId",
         cascade = CascadeType.ALL,
         orphanRemoval = true,
         fetch = FetchType.EAGER
   )
   @org.hibernate.annotations.OrderBy(clause = "sequence ASC")
   public List<FieldDefinitionValue> getListOfValues()
   {
      return listOfValues;
   }

当我从列表中删除一个项目时,当我保存父实体时,它不会从数据库中删除。

我创建了一个 CustomEntityDirtinessStrategy,这可能是它无法按预期工作的原因。在检查父实体的脏污时,我将实体报告为脏,即使唯一的更改是删除 listOfValues 中的一个或多个元素。

在 findDirty 中,我还将 listOfValues 报告为脏。

dirty = true 响应会导致不良行为(与主要问题不同,这就是为什么子实体删除根本不起作用的原因) - 对父表有不必要的更新调用。所以我怀疑我不应该将父实体本身标记为脏。我这样做是为了使子条目删除工作。

知道什么可能导致子移除失败吗?

下面是自定义的EntityDirtinessStrategy。我将它与 @DynamicUpdate 一起使用,以便 Hibernate 仅包含在其更新语句中实际更改的字段。

public class EntityDirtinessStrategy implements CustomEntityDirtinessStrategy
{
   private final Logger log = Logger.getLogger(EntityDirtinessStrategy.class);

   @Override
   public boolean canDirtyCheck(Object entity, EntityPersister persister, Session session)
   {
      return entity instanceof DirtyAware;
   }

   @Override
   public boolean isDirty(Object entity, EntityPersister persister, Session session)
   {
      return entity instanceof DirtyAware && ((DirtyAware)entity).isDirty();
   }

   @Override
   public void resetDirty(Object entity, EntityPersister persister, Session session)
   {
      if (entity instanceof DirtyAware)
         ((DirtyAware)entity).commitFields();
   }

   @Override
   public void findDirty(Object entity, EntityPersister persister, Session session, DirtyCheckContext dirtyCheckContext)
   {
      if (!(entity instanceof DirtyAware)) return;
      DirtyAware dirtyAware = (DirtyAware)entity;
      dirtyCheckContext.doDirtyChecking(
            attributeInformation -> {
               String propertyName = attributeInformation.getName();
               boolean dirty = dirtyAware.getDirtyFields().contains(propertyName);
               if (dirty) log.debug("Property "+propertyName+" is dirty.");
               return dirty;
            }
      );
   }
}

【问题讨论】:

  • CustomEntityDirtinessStrategy 是什么样子的?为什么需要它?

标签: hibernate jpa


【解决方案1】:

我确定了这里的问题所在。我没有说,因为我没有意识到这很重要,在我的 setX (List list) 方法(对于属性 X)上,我不是简单地存储列表然后将其传递回 getX 方法。

相反,我已将该列表中的内容复制到我的本地列表中。

Hibernate 要求实体直接使用 setX 集合,因为它实现了它提供的集合实现中的所有持久性逻辑。我设计了一种替代方法来检查集合的脏度(不是持久性所必需的,而是向我的 UI 代码识别实体是否被用户修改。)然后修改代码以接受并返回 setX 中的 Hibernate 集合和 getX 方法。现在可以了。

【讨论】: