Thymeleaf#sets.contains()尽管根据文档使用,但始终返回false

时间:2018-10-15 02:59:12

标签: spring spring-mvc spring-boot thymeleaf

序言

我有一个带有User实体和一组Role的spring boot应用程序。

在编辑用户模板上,我以<select>的倍数显示用户的角色。当使用一组User来渲染现有Role的视图时,我试图仅将集合中的角色标记为选中。

Thymeleaf为此提供了两个工具:

th:selected:期望为布尔值(选择了true)

#sets:它提供了一些类似于java.util.Set的有用方法,在这种情况下使用的是contains()

问题

将找到的User和所有可能的Role以HashSet的形式添加到模型时,使用#sets.contains()时,在使用找到的用户的角色和所有角色时始终返回false作为参数,因此在加载表单时不会选择用户的角色。

如果我使用th:selected="${{user.roles}}"表示法,则所有选项都被选中(甚至那些用户没有的选项)。

代码

用户

public class User
{

    private Long id;
    private String username;
    private String password;
    private String passwordConfirm;
    private Set<Role> roles;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Long getId()
    {
        return id;
    }

    public void setId(Long id)
    {
        this.id = id;
    }

    public String getUsername()
    {
        return username;
    }

    public void setUsername(String username)
    {
        this.username = username;
    }

    public String getPassword()
    {
        return password;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }

    @Transient
    public String getPasswordConfirm()
    {
        return passwordConfirm;
    }

    public void setPasswordConfirm(String passwordConfirm)
    {
        this.passwordConfirm = passwordConfirm;
    }

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "users_role", joinColumns = @JoinColumn(name = "users_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
    public Set<Role> getRoles()
    {
        return roles;
    }

    public void setRoles(Set<Role> roles)
    {
        this.roles = roles;
    }

}

角色

public class Role
{

private Long id;
private String name;
private Set<User> users;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId()
{
    return id;
}

public void setId(Long id)
{
    this.id = id;
}

public String getName()
{
    return name;
}

public void setName(String name)
{
    this.name = name;
}

@ManyToMany(mappedBy = "roles")
public Set<User> getUsers()
{
    return users;
}

public void setUsers(Set<User> users)
{
    this.users = users;
}
}

控制器

@Controller
@RequestMapping("/admin")
public class AdminController
{
    @Autowired
    UserService userService;

    @Autowired
    RoleService roleService;

    @RequestMapping("/user/edit/{id}")
    public String editUser(Model model, @PathVariable("id") long id)
    {
        User user = userService.findByUserId(id);
        HashSet<Role> foundRoles = roleService.getAllRoles();
        model.addAttribute("user", user);
        model.addAttribute("userRoles", foundRoles);
        return "admin/adminUserDetail";
    }

}

表格

<form role="form" th:action="@{/registration}" method="POST"
              th:object="${user}">
            <div th:if="${#fields.hasErrors('*')}">
                <div class="alert alert-danger" role="alert">
                    <h3 class="alert-heading">It seems we have a couple problems with your input</h3>
                    <li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
                </div>
            </div>
            <div class="form-group">
                <label>Username: </label> <input class="form-control" type="text" th:field="${user.username}"
                                                 placeholder="Username" name="username"/>

                <label>Password: </label> <input class="form-control" type="password" th:field="${user.password}"
                                                 placeholder="Password" name="password"/>

                <label>Password Confirm: </label> <input type="password"
                                                         th:field="${user.passwordConfirm}" class="form-control"
                                                         placeholder="Password Confirm"/>

                <select class="form-control" multiple="multiple">
                    <option th:each="role : ${userRoles}"
                            th:value="${role.id}"
                            th:selected="${#sets.contains(user.roles, role)}"
                            th:text="${role.name}">Role name
                    </option>
                </select>

                <button type="submit" class="btn btn-success">Update</button>
            </div>
        </form>

1 个答案:

答案 0 :(得分:1)

使用Set时,其中的对象必须同时实现hashCodeequals,因为它们用于确定对象是否已在Set中。除非是SortedSet使用Comparator或通过实现Comparable的对象表示的自然顺序。

由于您不执行任何一项操作,因此即使对于看似相同的contains实例,使用false的人也只会返回Role。因为根据合同,他们不是。

要修复在equalshashCode对象中实现UserRole方法的问题。

public class Role {

    public int hashCode() {
        return Objects.hash(this.name);
    }

    public boolean equals(Object o) {
        if (o == this) { return true; }
        if (o == null || !(o instanceof Role) ) { return false; }
        return Objects.equals(this.name, ((Role) o).name);
    }

}

遵循这些原则可以解决问题。