java 从集合中删除Hibernate @OneToMany不会在更新父实体时触发约束验证

amrnrhlw  于 2022-10-30  发布在  Java
关注(0)|答案(2)|浏览(127)

我有以下场景:
第一个
当我尝试执行以下操作时

@Transactional
  public void test() {
    var adGroup = adGroupRepository.findAll().stream().limit(1000).filter(adGr -> adGr.getId() == 142)
      .findFirst().get();
    var supervisor = adGroup.getSupervisors().iterator().next();
    adGroup.removeSupervisor(supervisor);
    adGroupRepository.save(adGroup);
  }

预期adGroupRepository.save(adGroup)将在adGroup上执行更新级联,而Set<SupervisorEntity> supervisors上的约束@Size(min = 2)将触发异常。我尝试调试DefaultFlushEntityEventListener::dirtyCheck,但我看到旧实体和新实体包含相同大小的supervisors。请任何人向我澄清我的错误所在以及如何触发此约束的更新/检查?
UPD在日志和数据库中,我看到主管已成功删除

2022-10-23 10:37:07.022 DEBUG [4b5430e5fcb074b6,4b5430e5fcb074b6] 93041 - [8080-exec-1] org.hibernate.SQL                : \n     delete \n     from\n         public.groups_supervisors \n     where\n         id=? 
Hibernate: 
    delete 
    from
        public.groups_supervisors 
    where
        id=?
gmxoilav

gmxoilav1#

对于延迟加载的关联Map,建议将验证注解放在getter上,而不是放在字段上,因为文档中提到了以下原因:
如果要验证延迟加载的关联,建议在关联的getter上设置约束条件。Hibernate ORM使用代理示例替换延迟加载的关联,代理示例在通过getter请求时进行初始化/加载。如果在这种情况下将约束条件设置在字段级别,则使用实际的代理示例,这将导致验证错误。
所以请尝试:

@Entity
@Data
@Table(name = "ad_groups")
public class KibanaAdGroupEntity extends AbstractEntity {

  @BatchSize(size = 10)
  @OneToMany(mappedBy = "kibanaAdGroup", cascade = {CascadeType.ALL}, orphanRemoval =true)
  Set<SupervisorEntity> supervisors;

   @NotNull 
   @Size(min = 2)
   Set<SupervisorEntity> getSupervisors(){
      return this.supervisors;
   }

}
t1rydlwq

t1rydlwq2#

似乎我发现了问题。这是由于Hibernate将注入到Set字段的类型。当您执行findAll时,默认情况下,Set字段将作为PersistenSet类型注入。这是您的业务逻辑端和Hibernate的dirty check mechanism之间的共享引用。因此,当您

adGroup.removeSupervisor(supervisor);
adGroupRepository.save(adGroup);

该操作触发DefaultFlushEntityEventListener::dirtyCheck。但由于final Object[] loadedState = entry.getLoadedState();保留shared reference,因此对于休眠,实体似乎未更改实体的状态。这就是未发生验证错误的原因(但子实体将按预期通过孤立删除删除)PS如果您将更改实体的任何字段,则将按预期执行验证

相关问题