使用复选框编辑用户的角色

Edit the roles of an user using checkboxes

我正在尝试使用 html 和 thymeleaf 编辑用户的角色。关键是一切正常,我可以编辑用户名、密码等,但是当涉及到角色时,我在保存列表时得到 null。我在互联网上找不到任何有用的东西,现在我已经为此苦苦挣扎了几个星期,我需要一种方法来编辑这些角色,因此该页面将 return 一个包含已检查角色的列表.

我这里有用户详细信息:

@Getter
@Setter
public class MyUserDetails implements UserDetails {
    private final User user;

    public MyUserDetails(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getRoles().stream()
                .map(role->new SimpleGrantedAuthority(role.getName()))
                .collect(Collectors.toList());
    }

    public int getId(){return this.user.getId();}

    public User getUser(){
        return this.user;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return user.isEnabled();
    }
}

这是用户:

@Builder
@Getter
@Setter
@Entity
@Table(name="user",schema = "public")
@NoArgsConstructor
@AllArgsConstructor
public class User {

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

    @NotEmpty(message = "You must provide a username!")
    @Size(min=5, max=30)
    private String username;

   @NotEmpty(message = "You must provide a password!")
    @ValidPassword
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String password;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private boolean enabled;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name="user_roles",
            joinColumns = @JoinColumn(name="user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id")
            )
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private List<Role> roles;

    @JoinColumn(name="last_played")
    private LocalDate lastPlayed;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    public List<String> getRoleNames(){
      return roles.stream()
                .map(Role::getName)
                .collect(Collectors.toList());
    }

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Reward> rewards;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", enabled=" + enabled +
                ", roles=" + roles +
                ", lastPlayed=" + lastPlayed +
                ", rewards=" + rewards +
                '}';
    }
}

这是角色 class:

@Getter
@Setter
@Entity
@Table(name="role",schema = "public")
public class Role {

    @Id
    @Column(name="role_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
}

控制器:

@Controller
@RequiredArgsConstructor
public class EditUserController {

    private final IUserDetailsService userService;
    private final RoleService roleService;

        @PostMapping(USERS_SAVE)
    public String saveEdit( @ModelAttribute(AttributeNames.USER) User user,
                            BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return EDIT_USER;
        }
        userService.updateUser(user);
        return REDIRECT+ USERS;
    }

    @GetMapping("/users/{id}")
    public String editUser(@PathVariable int id, Model model){

        List<Role> roleList = roleService.findAll();
        UserDetails user = userService.findUserById(id);

        model.addAttribute(AttributeNames.USER, user);
        model.addAttribute(AttributeNames.ROLE_LIST, roleList);
        return EDIT_USER;
    }

    @DeleteMapping(Mappings.USERS_DELETE_ID)
    public String deleteUser(@PathVariable int id){
            userService.deleteUser(id);
            return REDIRECT+USERS;
}
}

最后是角色的 html 部分:

 <div class="form-group row">
            <label class="col-form-label col-4">Roles</label>
            <div class="col-8">
               <th:block th:each="role:${roleList}">
                <input type="checkbox"
                       name="roles"
                       th:field="*{user.roles}"
                       th:value="${role.id}"
                       th:text="${role.name}" class="m-2"/>
               </th:block>
            </div>
        </div>

这里是带表格的图片,这个用户同时有user和admin两种角色,进入页面时都勾选了,但是如果我去update,角色会被修改为null。 你能帮我解决这个问题吗?

https://i.stack.imgur.com/DWpRw.png

映射不需要多对多,像这样使用单向:

@OneToMany(fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
@JoinTable(
        name = "user_role",
        joinColumns = @JoinColumn(
                name = "user_id",
                referencedColumnName = "id"
        ),
        inverseJoinColumns = @JoinColumn(
                name = "role_id",
                referencedColumnName = "id"
        ))
private List<Role> roles;

因为一个角色不需要为它正在被使用的相应用户提供参考。然后创建 table 'user_role'。之后,如果您更改角色并保存用户,则应在 user_role table.

中更新

更新 返回用户时你能试试这个吗:

@GetMapping(path = "/test")
@ResponseStatus(HttpStatus.OK)
public User test() {
    User user = // get user here by id
    //set roles there

    return user;
}

显然我的问题是我将 MyUserInterface class 添加到模型 insted 的用户。所以在控制器中我添加了这个:

MyUserDetails userDetails=userService.findUserById(id);
User user = userDetails.getUser();

现在 thymeleaf 可以直接访问用户列表。

 <th:block th:each="role:${roleList}">
                <input type="checkbox"
                       name="roles"
                       th:field="*{roles}"
                       th:value="${role.id}"
                       th:text="${role.name}" class="m-2"/>
               </th:block>