Search code examples
javaspring-bootthymeleaf

Spring Boot + Thymeleaf: How to show books and their authors in one list on the page


beginner here. I don't know how to show books and their authors in one list on the page. My first project was simplified and looked like this:

Book.java

@Entity
@Table(name = "book")
public class Book {

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

    @Column(name = "author")
    **private String author;**

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

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

    public Book() {
    }

    public Book(String author, String title, String isbn) {
        this.author = author;
        this.title = title;
        this.isbn = isbn;
    }
(...)
}

IndexController.java

@Controller
public class IndexController {

    private BookService bookService;

    @Autowired
    public IndexController(BookService bookService) {
        this.bookService = bookService;
    }

    @RequestMapping({"/", "", "/index"})
    public String showAll(Model model) {
        model.addAttribute("books", bookService.getAllBooks());

        return "index";
    }
}

index.html (thymeleaf)

<table>
    <thead>
    <tr>
        <th> Number</th>
        <th> Author</th>
        <th> Title</th>
        <th> ISBN</th>
    </tr>
    </thead>

    <tbody>
    <tr th:if="${books.isEmpty()}">
        <td colspan="3"> No records</td>
    </tr>

    <tr th:each="book, stat : ${books}" th:object="${book}">
        <!--        added for numbering purpose, variable stat is needed for iteration -->
        <td th:text="${stat.count}">1</td>
        <td th:text="*{author}"> Author</td>
        <td th:text="*{title}"> Title</td>
        <td th:text="*{isbn}"> ISBN</td>
    </tr>
    </tbody>
</table>

It was fine, data was working in my index.html list of books that way. But the problem is that a book might have many authors and many authors many books so I had to improve my model to look like that:

Book.java

@Entity
public class Book {

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

    private String title;
    private String isbn;

    @ManyToMany
    @JsonIgnore // removed wierd loop with data
    @JoinTable(name = "author_book", joinColumns = @JoinColumn(name = "book_id"),
            inverseJoinColumns = @JoinColumn(name = "author_id"))
    **private Set<Author> authors = new HashSet<>();**

    public Book() {
    }

    public Book(String title, String isbn) {
        this.title = title;
        this.isbn = isbn;
    }

    public Book(String title, String isbn, Set<Author> authors) {
        this.title = title;
        this.isbn = isbn;
        this.authors = authors;
    }
(...)
}

And had to create new Author.java entity:

@Entity
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String forename;
    private String surname;

    @ManyToMany(mappedBy = "authors")
    private Set<Book> books = new HashSet<>();

    public Author() {
    }

    public Author(String forename, String surname) {
        this.forename = forename;
        this.surname = surname;
    }

    public Author(String forename, String surname, Set<Book> books) {
        this.forename = forename;
        this.surname = surname;
        this.books = books;
    }
(...)
}

Could you please instruct me what should I do now? I want to show a list of books with authors in thymeleaf but I don't know how to create it in index.html so it would show all data. Do I have to change my IndexController (showAll() method)? Any advice would be greatly appreciated.

EDIT after Istiaque Hossain's post:

Now it is working but code does not look good (those 2 spans), advices how to make it better appreciated.

index.html

<table>
    <thead>
    <tr>
        <!--        th Number added for numbering purpose-->
        <th> Number</th>
        <th> Author_Name</th>
        <th> Author_Surn</th>
        <th> Title</th>
        <th> ISBN</th>
    </tr>
    </thead>

    <tbody>
    <tr th:if="${books.isEmpty()}">
        <td colspan="3"> No records</td>
    </tr>

    <tr th:each="book, stat : ${books}" th:object="${book}">
        <!--        added for numbering purpose, variable stat is needed for iteration -->
        <td th:text="${stat.count}">1</td>
        <td>
            <span th:each="author : ${book.authors}">
                <span th:utext="${author.forename}" />
            </span>
        </td>
        <td>
            <span th:each="author : ${book.authors}">
                <span th:utext="${author.surname}" />
            </span>
        </td>
        <td th:text="*{title}"> Title</td>
        <td th:text="*{isbn}"> ISBN</td>
    </tr>
    </tbody>
</table>

Looks like that: enter image description here


Solution

  • No need to change in your controller. you need little change in your html page

    try this code it may be help you...

           <tr th:each="book, stat : ${books}" th:object="${book}">
                    <!--        added for numbering purpose, variable stat is needed for iteration -->
                    <td th:text="${stat.count}">1</td>
        ///////////////////////////////////////////////
                    <td> 
                        <span th:each="author, iterator1: ${book.authors}" th:remove="tag">
                             <span th:utext="${iterator1.index+1+'. ' + author.forename+' <br/>'}" />
                        </span>
                     </td>
        ////////////////////////////////////
                    <td th:text="*{title}"> Title</td>
                    <td th:text="*{isbn}"> ISBN</td>
                </tr>