我开始使用Spring Data JPA在Spring Boot应用程序上进行工作,以在用户和角色之间建立ManyToMany关系。

此关系在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", "[email protected]", "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", "[email protected]", "pass");
    //Save user
    userRepository.save(user);

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

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

最佳答案

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

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

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

  • (如果User和UserRole都是新的)-保存User时您将获得持久操作,并且级联将持久保存到UserRole,因为UserRole是新功能,因此可以很好地工作。
  • (如果用户是新用户,但存在UserRole)-在保存User时,您将再次执行持久操作,并且级联到UserRole,由于UserRole已经存在,这将导致错误!

  • 如果可以保证添加到与用户的关系中的任何UserRole始终存在,则只需从级联选项中删除Cascade.Persist。否则,我相信在上述两种情况下保存用户时,都必须通过自定义存储库和EntityManager使用合并。

    关于您使用Cascade.ALL的情况,在这种情况下这可能没有意义,因为您已经有了@ManyToMany关系,所以将ALL从User层叠到UserRole可能会产生一些不良后果(例如Cascade.Remove每次都会删除UserRole)用户已删除)。

    关于java - Spring数据JPA分离实体,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31037503/

    10-13 04:56