Thymeleaf # sets.contains () всегда возвращает false, несмотря на использование в соответствии с документацией - PullRequest
0 голосов
/ 15 октября 2018

Предисловие

У меня есть приложение с пружинной загрузкой с сущностью User с набором Role.

В редактируемом шаблоне пользователя яотображение ролей пользователя с кратным числом <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 голосов
/ 16 октября 2018

При использовании Set объект там должен реализовывать как hashCode, так и equals, так как он используется для определения, находится ли объект уже в Set.Если это не SortedSet, который использует либо Comparator, либо естественный порядок, выраженный вашим объектом, реализующим Comparable.

Поскольку вы не делаете ни одного из тех, кто использует contains, просто всегда вернет false даже для, казалось бы, того же экземпляра Role.Потому что согласно контракту они не являются.

Исправить реализацию метода equals и hashCode в объекте User и Role.

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);
    }

}

Что-то в этом роде должно сработать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...