Java 在Spring Security中添加新用户时获取异常

Java 在Spring Security中添加新用户时获取异常,java,spring-boot,spring-security,spring-data-jpa,spring-data,Java,Spring Boot,Spring Security,Spring Data Jpa,Spring Data,我有一个用户类、角色类和用户角色类。现在,每次我尝试添加一个新用户,其中包含一组已经存在的角色,它都会抛出一个唯一的错误,这是正确的。但理想情况下,如果新角色已经存在,它不应该试图保存它。下面我添加了我所有的表和保存方法 @Table(name = "t_user") @Data public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(na

我有一个用户类、角色类和用户角色类。现在,每次我尝试添加一个新用户,其中包含一组已经存在的角色,它都会抛出一个唯一的错误,这是正确的。但理想情况下,如果新角色已经存在,它不应该试图保存它。下面我添加了我所有的表和保存方法

@Table(name = "t_user")
@Data
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "mobile_number")
    private String mobileNumber;

   
    @Column(name = "email")
    private String email;

   
    @Column(name = "first_name")
    private String firstName;

    @Size(max = 100)
    @NotBlank(message = "Last name can not be empty")
    @Column(name = "last_name")
    private String lastName;

    @Column(name = "is_archived")
    private Boolean isArchived = false;

    @Column(name = "qualification")
    private String qualification;

    @JsonIgnore
    @Column(name="password")
    private String password;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "t_user_role", joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
            inverseJoinColumns = {
                    @JoinColumn(name = "role_id", referencedColumnName = "id")})
    private Set<Role> roles = new HashSet<>();


}

保存用户的方法:

User newUser = new User();
        newUser.setFirstName(user.getFirstName());
        newUser.setLastName(user.getLastName());
        newUser.setEmail(user.getEmail());
        newUser.setMobileNumber(user.getPassword());
        newUser.setPassword(bcryptEncoder.encode(user.getPassword()));
        newUser.setRoles(user.getRoles());
        return userRepository.save(newUser);
    }
{
    "firstName":"first",
    "lastName":"name",
    "email":"email@gmail.com",
    "mobileNumber":"1110122223",
    "password":"1234567890",
    "roles":[{
        "name":"ADMIN"
    }]
}
下面是创建用户的post请求格式:

User newUser = new User();
        newUser.setFirstName(user.getFirstName());
        newUser.setLastName(user.getLastName());
        newUser.setEmail(user.getEmail());
        newUser.setMobileNumber(user.getPassword());
        newUser.setPassword(bcryptEncoder.encode(user.getPassword()));
        newUser.setRoles(user.getRoles());
        return userRepository.save(newUser);
    }
{
    "firstName":"first",
    "lastName":"name",
    "email":"email@gmail.com",
    "mobileNumber":"1110122223",
    "password":"1234567890",
    "roles":[{
        "name":"ADMIN"
    }]
}

我不想插入的角色,如果存在,这应该是理想的。但这是我在使用角色实现spring安全性时发现的标准方法,您的请求缺少角色的
id
。As
id
不存在。Spring尝试在
role
表中添加新角色

{
    "firstName":"first",
    "lastName":"name",
    "email":"email@gmail.com",
    "mobileNumber":"1110122223",
    "password":"1234567890",
    "roles":[{
        "id" : "" // ID must be present here.
        "name":"ADMIN"
    }]
}
或者从
角色->名称
,您可以从角色表中获取
角色
实体/obj,并在
用户
对象中进行设置

[更新2]:

@ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
  @ToString.Exclude
  private Set<Role> roles = new HashSet<>();
@ManyToMany(cascade=CascadeType.DETACH,fetch=FetchType.LAZY)
@ToString.Exclude
私有集角色=新HashSet();

您需要将级联类型从“全部”更改为“分离”。请参阅:
ALL
表示如果保存用户,角色也将被保存,如果删除用户,角色也将被删除。这不是我们想要的。您只需要使用“角色”,而不需要以任何方式操纵“角色”表记录。

就我所知,您的要求是:

  • 用户实体
  • 角色实体,每个用户具有多个角色
  • 如果从客户端传递角色,则仅当数据库中不存在该角色时才保存该角色,否则要使用现有角色(更新:根据评论和我的意见,这绝不是一件理想的事情)
  • 在您的情况下,我建议让Spring处理用户->角色关系,如下所示:

    public class User {
    
      ... all fields
      @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
      @ToString.Exclude
      private Set<Role> roles = new HashSet<>();
    }
    
    public class Role {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
        private String name;
    }
    
    公共类用户{
    …所有领域
    @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
    @ToString.Exclude
    私有集角色=新HashSet();
    }
    公共阶级角色{
    @身份证
    @GeneratedValue(策略=GenerationType.IDENTITY)
    私有整数id;
    私有字符串名称;
    }
    
    在角色存储库中,您需要一个方法:
    可选的findByName(字符串名称)

    在层之间(最好在服务层中),尝试以下操作:

    public Map<String, Object> addUser(User user) {
        // perform validations
    
        user.getRoles().forEach(role -> {
            Role existing = roleRepository.findByName(role.getName())
                           .orElse(new Role(role.getName())); // using optional to create new role with name passed in from the client
            if (existing.getId() != null) user.setRole(existing); // UPDATE: NOT IDEAL
        });
        
        
        ... other tasks
        userRepository.save(user); // this all saves the correct role
        return yourResponseMap;
    }
    
    publicmap addUser(用户){
    //执行验证
    user.getRoles().forEach(角色->{
    Role existing=roleRepository.findByName(Role.getName())
    .orElse(新角色(Role.getName());//使用optional以从客户端传入的名称创建新角色
    if(existing.getId()!=null)user.setRole(existing);//更新:不理想
    });
    …其他任务
    userRepository.save(user);//这将保存正确的角色
    返回您的响应映射;
    }
    
    其他说明:

  • 我们通常更喜欢让回访者保持懒惰,而不是渴望。但有些情况下,你可能需要急切的检索,所以这取决于你
  • 在我看来,让Spring数据JPA处理第三个表更方便

  • org.hibernate.PersistentObjectException:当您试图保存从客户端直接传入的角色而不将其加载到应用程序时(请参阅“添加用户”的服务层方法),会发生传递到persistent的分离实体
  • 检查此项,您可能会发现它很有用

  • 你能添加例外吗?如果没有itLombok@Data合并@EqualsAndHashCode(集合所需),我们就不可能有想法。你不想那样。在类中,typ alt insert并实现Equals()和HashCode()。可能还有另一个问题,但这引起了我的注意。出现的异常是数据完整性冲突异常,因为角色管理员已存在使用
    save
    方法调用将在数据库中插入记录,并且由于唯一约束,它将抛出错误。如果要防止重复值,则可以在调用save方法之前进行检查并显示自定义消息,但我正在对t_user_角色表而不是角色表org.hibernate.PersistentObjectException:传递给persist的分离实体:com.user.model.role。我在请求体中持久化对象时遇到此异常,我添加了存在的id,并尝试进行rest调用Yes写入,当您尝试保存用户时,spring会自动尝试保存角色(如果角色id不存在->尝试添加新记录),(如果存在id->尝试更新记录)。我们需要将用户中的角色字段标记为“分离”。为此->您需要更改角色集@ManytoMany注释中的级联类型。[cascade=CascadeType.DETACH]请添加注释以对答案进行向下投票。您的
    addUser
    函数中存在两个问题,首先,它不支持用户中的多个角色,这是一个次要问题。其次,我们不应该在添加用户时添加新角色。角色通常只是预定义的。这似乎属于一类重大问题。我很抱歉,但你的回答对解决问题毫无帮助。您刚刚添加了额外的“惰性”加载。这在这种情况下仍然没有帮助。用户必须具有角色。”“LAZY”是我们在场景中使用的,在场景中,我们有额外的属性,这些属性可能有较大的集合,或者有变大的趋势。@AmanGarg的问题是:“但理想情况下,如果新角色已经存在,它不应该试图保存它。下面我将添加我的所有表和保存方法。”表示当角色不存在时,他会保存它。我同意他不应该保留这个新角色。关于处理多行,我发布了一个更新,但肯定有人谁在这个阶段编码可以确定他想如何修改代码。这不是一个能够满足所有情况的即插即用解决方案。然后需要添加验证。Cascade.ALL在此无效。感谢您的反馈。如前所述,