'Method contains does not work in <form:option>

I have a project that represents Library. On edit page of book, I want that co-authors will be selected, if this book has these authors. For example, like in the screenshots: booklistpage, editbookpage

I used this code to do that

    <tr>
        <td>Co-authors</td>
        <td>
            <form:select class="form-select" path="authorsSet" multiple="true">
                <c:forEach items="${authors}" var="author">
                    <c:set var="authorName" value="${author.name} ${author.surname}"/>
                    <form:option value="${author.authorId}" label="${authorName}" selected="${book.authorSet.contains(author)? 'selected' : '' }"/>
                </c:forEach>
            </form:select></td>
    </tr>

But it doesn't work. I overrode method .equals(), because I thought that problem may happen there. It also didn't help. There is my model and controller classes

BookController.class

package com.library.controller;

import com.library.AuthorEditor;
import com.library.model.Author;
import com.library.model.Book;
import com.library.service.AuthorService;
import com.library.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.Set;

@Controller
@RequestMapping("/books")
public class BookController {

    @Autowired
    private BookService bookService;

    @Autowired
    private AuthorService authorService;

    @Autowired
    private AuthorEditor authorEditor;

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Author.class, this.authorEditor);
    }

    @GetMapping
    public String allBooks(Model model) {
        model.addAttribute("books", bookService.findAll());
        return "book_list";
    }

    @GetMapping("/add")
    public String showForm(Model model) {
        Book book = new Book();
        model.addAttribute("book", book);
        model.addAttribute("authors", authorService.findAll());
        return "book_add";
    }

    @PostMapping("/add")
    public String addBook(@Valid @ModelAttribute Book book) {
        bookService.addBook(book);
        return "redirect:/books";
    }

    @GetMapping("/{id}")
    public String getBook(Model model, @PathVariable Long id) {
        Book book = bookService.findBookById(id);
        model.addAttribute("book", book);
        model.addAttribute("authors", book.getAuthorSet());
        return "book_details";
    }

    @GetMapping("delete/{id}")
    public String deleteBook(@PathVariable Long id) {
        Book book = bookService.findBookById(id);
        bookService.deleteBook(book);
        return "redirect:/books";
    }

    @RequestMapping("/edit/{id}")
    public String editBook(@PathVariable("id") long id, Model model){
        model.addAttribute("book", this.bookService.findBookById(id));
        model.addAttribute("title", this.bookService.findBookById(id).getTitle());
        model.addAttribute("mainAuthor", this.bookService.findBookById(id).getMainAuthor());
        //all authors
        model.addAttribute("authors", authorService.findAll());
        //all authors of book
        model.addAttribute("authorsSet", bookService.findBookById(id).getAuthorSet());
        model.addAttribute("amountOfCopies", bookService.findBookById(id).getAmountOfCopies());


        return "book_edit";
    }

    @PostMapping("/edit/{id}")
    public String editBook(@PathVariable("id") long id, @RequestParam String title, @RequestParam Integer amountOfCopies, @RequestParam Author mainAuthor, @RequestParam(required = false)Set<Author> authorsSet) {
        Book book = this.bookService.findBookById(id);
        book.setTitle(title);
        book.setAmountOfCopies(amountOfCopies);
        book.setMainAuthor(mainAuthor);
        book.setAuthorSet(authorsSet);
        this.bookService.updateBook(book);
        return "redirect:/books";
    }

}

Book.class

package com.library.model;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotEmpty;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "books")
public class Book {

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

    @Column(name = "title", nullable = false)
    @NotEmpty(message = "Please, enter tittle of the book")
    private String title;

    @Column(name = "amount_of_copies", nullable = false)
    private Integer amountOfCopies;

    @ManyToOne
    @JoinColumn(name = "main_author")
    private Author mainAuthor;

    @ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.EAGER)
    @OnDelete(action = OnDeleteAction.NO_ACTION)
    @JoinTable(
            name = "books_authors",
            joinColumns = {@JoinColumn(name = "book_id")},
            inverseJoinColumns = {@JoinColumn(name = "author_id")}
    )
    Set<Author> authorSet = new HashSet<>();

    public Set<Author> getAuthorsSet() {
        return authorSet;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return Objects.equals(bookId, book.bookId);
    }
}

Author.class

package com.library.model;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import java.util.HashSet;
import java.util.Set;

@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "authors")
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "author_id")
    private long authorId;

    @Column(name = "first_name", nullable = false)
    String name;

    @Column(name = "last_name", nullable = false)
    String surname;

    @ManyToMany(mappedBy = "authorSet", cascade = CascadeType.PERSIST)
    private Set<Book> bookSet = new HashSet<>();

}

book_edit.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
    <title>Book</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body>
<div class="container">
    <h1>Edit book information</h1>
    <table class="table table-borderless">
        <form:form method="post" modelAttribute="book">
            <tr>
                <td>Title</td>
                <td><form:input path="title" value="${book.title}" class="form-control"/></td>
            </tr>

            <tr>
                <td>Main author</td>
                <td>
                    <form:select class="form-select" path="mainAuthor">
                        <c:forEach items="${authors}" var="author">
                            <c:set var="authorName" value="${author.name} ${author.surname}"/>
                            <form:option value="${author.authorId}" label="${authorName}" selected="${author.authorId == book.mainAuthor.authorId? 'selected' : '' }"/>
                        </c:forEach>
                    </form:select></td>

            </tr>
            <tr>
                <td>Co-authors</td>
                <td>
                        <%--FIXME selected authors is necessary--%>
                    <form:select class="form-select" path="authorsSet" multiple="true">
                        <c:forEach items="${authors}" var="author">
                            <c:set var="authorName" value="${author.name} ${author.surname}"/>
                            <form:option value="${author.authorId}" label="${authorName}" selected="${book.authorSet.contains(author)? 'selected' : '' }"/>
                        </c:forEach>
                    </form:select></td>
            </tr>
            <tr>
                <td>Amount of copies</td>
                <td><form:input path="amountOfCopies" class="form-control" value="${book.amountOfCopies}"/>
                </td>
            </tr>
            <tr>
                <td>
                    <button class="btn btn-outline-dark" type="submit">Update</button>
                </td>
            </tr>

        </form:form>
    </table>

    <a href="/library/books">Back to List</a><br>
    <a href="/library">Home</a>
</div>
</body>
</html>

Could you, please, help me to find the problem.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source