【问题标题】:Spring data jpa detached entitySpring data jpa 分离实体
【发布时间】:2015-09-11 07:14:33
【问题描述】:

我开始使用 Spring Data JPA 开发 Spring Boot 应用程序,以在用户和角色之间建立多对多关系。

这种关系在 User 类中定义如下:

@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name="user_role", joinColumns = {@JoinColumn(name="user_id")}, inverseJoinColumns = {@JoinColumn(name="role_id")})
private Set<UserRole> roles;

我使用以下方法创建角色:

@Transactional
private void generateSeedRoles() {
    UserRole adminRole = new UserRole(RoleEnum.ADMIN.toString());
    userRoleRepository.save(adminRole);

    UserRole userRole = new UserRole(RoleEnum.USER.toString());
    userRoleRepository.save(userRole);
}

之后为用户分配角色失败:

@Transactional
private void generateSeedUsers() {
    UserRole adminRole = userRoleRepository.findUserRoleByRoleName("ADMIN");

    User user = User.createUser("user1", "user1@user.com", "pass");
    user.setRoles(new HashSet<UserRole>(Arrays.asList(adminRole)));

    userRepository.save(user);
}

抛出以下异常(为便于阅读而格式化):

org.springframework.dao.InvalidDataAccessApiUsageException: 
detached entity passed to persist: co.feeb.models.UserRole; 
nested exception is org.hibernate.PersistentObjectException: 
detached entity passed to persist: co.feeb.models.UserRole

但是,如果在创建关系之前保存了用户,则可以正常工作:

@Transactional
private void generateSeedUsers() {
    UserRole adminRole = userRoleRepository.findUserRoleByRoleName("ADMIN");

    User user = User.createUser("user1", "user1@user.com", "pass");
    //Save user
    userRepository.save(user);

    //Build relationship and update user
    user.setRoles(new HashSet<UserRole>(Arrays.asList(adminRole)));
    userRepository.save(user);
}

对我来说,必须两次保存/更新用户似乎有些不合理。有什么方法可以在不先保存用户的情况下将接收到的角色分配给新用户

【问题讨论】:

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


【解决方案1】:

当您保存实体时,Spring 会在内部检查实体是否是新的(我忘记了确切的机制),并发出持久化(如果是新的)或合并(如果存在)。

由于您已经使用 Cascade.ALL 定义了 User 到 UserRole 的关系,因此来自 User 的任何持久化/合并也将级联到 UserRole。

鉴于 Spring 如何实现保存,以及上面的级联行为,这里有一些需要考虑的场景:

  • 如果 User 和 UserRole 都是新的 - 您将在保存 User 时获得一个持久化操作,并将级联持久化到 UserRole,由于 UserRole 是新的,因此工作正常。
  • 如果 User 是新用户但 UserRole 已存在 - 在保存 User 时,您将再次获得持久化操作,并且级联持久化到 UserRole,这会导致错误,因为 UserRole 已经存在!

如果您可以保证您添加到与 User 的关系中的任何 UserRole 始终存在,您可以简单地从您的级联选项中删除 Cascade.Persist。否则,我相信在上述两种情况下保存用户时,您将不得不使用合并(通过自定义存储库和 entityManager)。

关于您对 Cascade.ALL 的使用,在这种情况下可能没有意义,因为您有一个 @ManyToMany 关系,将 ALL 从 User 级联到 UserRole 可能会产生一些不良影响(例如 Cascade.Remove 会删除每次删除用户时的 UserRole)。

【讨论】:

  • 将 CascadeType.ALL 更改为 {CascadeType.MERGE, CascadeType.DETACH} 并且有效!非常感谢!那些 CascadeTypes 是必需的还是我错过了什么?
  • 需要哪个级联取决于您的需求/业务逻辑,但在这种情况下,合并和分离似乎是合适的级联。如果您发现此答案对您有帮助,请随时接受该答案。谢谢。
  • 成功了!是否存在设置“除 PERSIST 之外的所有”级联类型的方法? CascadeType.ALL - CascadeType.PERSIST 那就太好了! xD
猜你喜欢
  • 1970-01-01
  • 2021-04-19
  • 2015-08-10
  • 1970-01-01
  • 2019-09-30
  • 2019-10-01
  • 2020-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多