在 Spring Data Jpa 中通过级联持久保存关联的分离实体。可以吗?

问题描述 投票:0回答:1

如果我想在 Spring Data Jpa 中保留一个“新”实体和一个“旧”关联实体,该怎么办?让我告诉你我的意思

  1. 收到注册请求,这是我的处理方式
    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

如何避免这种情况?

java spring hibernate jpa spring-data
1个回答
0
投票

我的结论是:

  1. 无论如何坚持一个分离的实体都会抛出问题
  2. Spring Data 的
    if
    检查将评估为
    true
    无论如何(如果你保留一个新实体)
  3. 角色的级联持久化不会经过相同的检查,因为它将绕过 Spring Data 框架
  4. 意味着
    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;
    }
© www.soinside.com 2019 - 2024. All rights reserved.