如果我想在 Spring Data Jpa 中保留一个“新”实体和一个“旧”关联实体,该怎么办?让我告诉你我的意思
public Mono<ServerResponse> signUp(ServerRequest request) {
return request.bodyToMono(UserDto.class)
.map(userMapper::toUser)
.map(userService::encodePassword)
.map(userService::addDefaultRoles)
.map(userService::save)
.map(this::toAuthenticatedUpat)
.map(tokenService::generateTokenFor)
.transform(jwt -> ServerResponse.status(HttpStatus.CREATED).body(jwt, String.class));
}
重要的部分是
addDefaultRoles()
和save()
@Override
public User save(User user) {
return userRepository.save(user); // Spring Data's repository
}
@Override
@Transactional // the only @Transactional method in UserServiceImpl
public User addDefaultRoles(User user) {
Optional<Role> userRoleOptional = roleService.findByAuthority(Role.USER);
Role userRole = userRoleOptional.orElse(new Role(Role.USER));
userRole.addUser(user);
user.addRole(userRole);
return user;
}
// @Entity etc.
public class User implements UserDetails {
// ...
@ManyToMany(cascade = CascadeType.PERSIST) // note this
@JoinTable(name = "users_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> authorities;
// @Entity etc.
public class Role implements GrantedAuthority {
public static final String USER = "user";
// ...
@ManyToMany(mappedBy = "authorities")
private Set<User> users;
假设我有一个干净的记录,并且数据库没有任何记录。第一个注册请求顺利完成:
userRoleOptional
为空,新用户和新的 USER
角色都被保留
// Spring's SimpleJpaRepository
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null");
if (entityInformation.isNew(entity)) { // true
entityManager.persist(entity); // persist cascaded to role
return entity;
} else {
return entityManager.merge(entity);
}
}
然后出现第二个注册请求。这一次,我有一个新用户和一个旧角色 (
USER
)。 entityInformation.isNew(entity)
被评估为 true
,再次调用 persist()
并级联到现在被视为分离的角色,并且会发生这种情况
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.example.tokenservice.data.entity.Role
如何避免这种情况?
我的结论是:
if
检查将评估为 true
无论如何(如果你保留一个新实体)save()
使用关联的分离实体创建一个新实体将不可避免地抛出这是一个解决方法。移除级联...
@ManyToMany
@JoinTable(name = "users_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> authorities;
...并预先保留角色(如果尚不存在)手动
@Override
@Transactional
public User addDefaultRoles(User user) {
Optional<Role> userRoleOptional = roleService.findByAuthority(Role.USER);
Role userRole = userRoleOptional.orElseGet(() -> roleService.save(new Role(Role.USER)));
userRole.addUser(user);
user.addRole(userRole);
return user;
}