Spring 数据 jpa 分离实体
Spring data jpa detached entity
我开始使用 Spring Data JPA 开发 Spring 引导应用程序,以在用户和角色之间设置多对多关系。
此关系在用户 class 中定义如下:
@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);
}
必须 save/update 用户两次对我来说似乎有点不合理。有什么方法可以在不先保存用户的情况下将接收到的角色分配给 新用户?
当您保存您的实体时,Spring 会在内部检查该实体是否是新的(我忘记了确切的机制),并发出持久化(如果是新的)或合并(如果存在)。
由于您已经使用 Cascade.ALL 定义了 User 到 UserRole 的关系,因此来自 User 的任何 persists/merge 也将级联到 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)。
我开始使用 Spring Data JPA 开发 Spring 引导应用程序,以在用户和角色之间设置多对多关系。
此关系在用户 class 中定义如下:
@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);
}
必须 save/update 用户两次对我来说似乎有点不合理。有什么方法可以在不先保存用户的情况下将接收到的角色分配给 新用户?
当您保存您的实体时,Spring 会在内部检查该实体是否是新的(我忘记了确切的机制),并发出持久化(如果是新的)或合并(如果存在)。
由于您已经使用 Cascade.ALL 定义了 User 到 UserRole 的关系,因此来自 User 的任何 persists/merge 也将级联到 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)。