【问题标题】:Hibernate validator causing many to many relationship to not save休眠验证器导致多对多关系无法保存
【发布时间】:2019-03-06 21:29:18
【问题描述】:

我在用户和角色之间存在多对多关系。我在用户中设置的角色上有一个自定义休眠验证约束。

@PostConstruct 中,我使用来自 spring-data-jpa 的标准 JpaRepository 将初始角色(ADMIN、USER)保存到数据库中。然后我使用管理员角色创建一个初始用户。

如果我没有自定义验证,则关联会正确保存,并且我会在 user_role 连接表中看到一个条目。如果我有验证,用户被插入到用户表中,但没有进入user_role 表。返回的实体具有角色集中的角色,但它没有保存到D B。代码总结如下。我无法理解使用 RoleRepo 获取所有角色如何以任何方式破坏保存,但确实如此。

class User {
  @Id
  String username;

  @ValidOption
  @ManyToMany(cascade = {CascadeType.ALL //for example}, fetch=FetchType.EAGER)
  Set<Role> roles;
}

class Role {
  @Id
  String name;
}

class CustomValidator implements ConstraintValidator<ValidOption, Object> {
  RoleRepository roleRepo; //injected by spring... have spring factory

  @Override
  public boolean isValid(Object value, ConstraintValidatorContext context){
    roleRepo.findAll() //<-------------- THIS CALL BREAKS THE SAVE
    return true;
  }
}

@Component
class UserCreator {
  RoleRepository roleRepo;
  UserRepo userRepo;

  @PostConstruct
  void setup(){
    Role admin = roleRepo.saveAndFlush(new Role('ADMIN'));
    roleRepo.saveAndFlush(new Role('USER'));

    User user = new User('admin', Collections.singleton(admin));
    userRepo.save(user); //<------ DOES NOT INSERT ADMIN INTO USER_ROLE JOIN TABLE
  }
}

如果我删除自定义验证器,这 100% 完全符合我的预期。如果我不在 PostConstruct 中运行它并将其安排在不同的线程中,它也可能有效,我需要检查一下。

具有可重现的失败测试用例的项目:https://github.com/tjhelmuth/SPR-22533/blob/master/src/test/java/spr22533/bug/BugExample.java

【问题讨论】:

  • 你有什么输出给我们吗?我不太清楚“打破保存”。
  • 在异常中没有负输出。我在我的描述中加粗了负面输出。用户被保存到“用户”表中,但没有插入到 user_role 连接表中。我从“保存”调用返回的实体中有 ADMIN 角色,但它没有保存在数据库中。

标签: java spring hibernate spring-data-jpa hibernate-validator


【解决方案1】:

在验证期间访问EntityManager 不能保证在验证期间有效。

验证发生在“生命周期回调方法”中。 对于这些,适用以下限制(Java 持久性规范 2.2;第 3.5.2 节生命周期回调方法):

一般来说,可移植应用程序的生命周期方法不应调用 EntityManager 或查询操作、访问其他实体实例或修改同一持久性上下文中的关系。生命周期回调方法可能会修改调用它的实体的非关系状态。

要使其正常工作,请使用单独的 EntityManager,这当然可能会因运行不同的事务而受到不同的更改集的影响。

另请参阅:Correct way to do an EntityManager query during Hibernate Validation

【讨论】:

  • 好的,谢谢您,我唯一的问题是我非常喜欢您使用 Spring Data 进行的工作,并且希望像往常一样使用我的 JpaRepository 实现。如果我在验证器中 fork 一个新线程,我将能够获得一个新的 EM 和 JpaRepository 实例吗?
  • 感谢您的客气话。开始一个新线程可能会起作用,但感觉很糟糕。我想知道是否可以为验证器创建一个专用范围。或者,可以以编程方式构建存储库。当然,这取决于您在验证器中实际想要做什么,只需创建一个新的EntityManager 并直接使用它可能是更简单的方法。如果您有兴趣探索替代方案,请创建一个新问题,因为完整的答案不适合评论。
猜你喜欢
  • 1970-01-01
  • 2023-03-17
  • 2015-12-22
  • 1970-01-01
  • 1970-01-01
  • 2018-12-23
相关资源
最近更新 更多