I am trying to create an ul
which has a li
for each review
in the Set<Review> reviews
from the book
object that I send back from the server. The result is seemingly a massive internal server error, I get a very long stack-trace printed out to the terminal, I have no idea what might be the problem. If I comment out the ul
block, everything works fine.
The error (opens new link, pastebin) (not the full error, it did not fit in VSCODE terminal.
book.html
<div class="container" style="width: 100%; padding-top: 25px; display: flex; justify-content: center;">
<div class="card" style="width: 18rem;">
<img class="card-img-top" src="..." alt="Card image cap">
<div class="card-body">
<h5 th:text="${book.name}" class="card-title">Book name here.</h5>
<p th:text="${book.author}" class="card-text">Author name here.</p>
<p th:text="${book.isbn}" class="card-text">ISBN number here.</p>
</div>
<ul class="list-group list-group-flush">
<li th:each="review : ${book.reviews}" th:text="${review.review}" class="list-group-item">Review goes here.</li>
</ul>
</div>
</div>
BookController.java
package com.domain.congregentur.book;
import ...
@Controller
@RequestMapping("api/books")
public class BookController {
public BookService bookService;
@Autowired
public BookController(BookService bookService) {
this.bookService = bookService;
}
...
@GetMapping("/{id}")
public String find(@PathVariable("id") Long id, Model model) {
Optional<Book> book = bookService.findById(id);
if (book.isPresent()) {
model.addAttribute("book", book.get());
return "book";
}
return "books";
}
...
}
Book.java
package com.domain.congregentur.book;
import ...
@Entity
@Table(name = "Books")
@EqualsAndHashCode
@ToString
@Getter
@Setter
public class Book implements Serializable {
@Id
@SequenceGenerator(name = "book_sequence", sequenceName = "book_sequence", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_sequence")
private Long id;
@NotNull
private String isbn;
@NotNull
private String name;
@NotNull
private String author;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "book")
private Set<Review> reviews;
Book() {
}
public Book(Long id, String isbn, String name, String author) {
this.id = id;
this.isbn = isbn;
this.name = name;
this.author = author;
}
public Book(String isbn, String name, String author) {
this.isbn = isbn;
this.name = name;
this.author = author;
}
public Book updateWith(Book book) {
return new Book(
this.id,
book.isbn,
book.name,
book.author);
}
}
Review.java
package com.domain.congregentur.review;
import ...
@Entity
@Table(name = "Reviews")
@EqualsAndHashCode
@ToString
@Setter
@Getter
public class Review {
@Id
@SequenceGenerator(name = "review_sequence", sequenceName = "review_sequence", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "review_sequence")
private Long id;
@NotNull
private String review;
@ManyToOne
@JoinColumn(name = "isbn", referencedColumnName = "isbn")
private Book book;
Review() {
}
public Review(String review, Book book) {
this.review = review;
this.book = book;
}
}
Might be worth mentioning that this is how I instantiate some random test-data to my db to test with, I don't know if this is an appropriate way of doing this and it may or may not be relevant to the problem. Feel free to educate me on this.
DataLoader.java
package com.domain.congregentur.dataloader;
import ...
@Component
public class DataLoader implements ApplicationRunner {
private BookRepository bookRepository;
private ReviewRepository reviewRepository;
@Autowired
public DataLoader(BookRepository bookRepository, ReviewRepository reviewRepository) {
this.bookRepository = bookRepository;
this.reviewRepository = reviewRepository;
}
public void run(ApplicationArguments args) {
Book b1 = new Book("9780812969641", "In Search of Lost Time", "Marcel Proust");
Book b2 = new Book("9781772267143", "Swann's Way", "Marcel Proust");
Book b3 = new Book("9780099469698", "Don Quixote", "Miguel de Cervantes");
bookRepository.saveAll(List.of(b1, b2, b3));
List<Review> reviews = List.of(
new Review("Book1 -> Review1", b1),
new Review("Book1 -> Review2", b1),
new Review("Book2 -> Review1", b2),
new Review("Book2 -> Review2", b2),
new Review("Book3 -> Review1", b3),
new Review("Book3 -> Review2", b3));
reviewRepository.saveAll(reviews);
}
}
After having looked at the stack trace for a bit, it is seemingly repeating the following part but with slightly different numbers at some specific places.
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:591) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:458) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at com.domain.congregentur.book.Book.hashCode(Book.java:26) ~[classes/:na]
at com.domain.congregentur.review.Review.hashCode(Review.java:22) ~[classes/:na]
at java.base/java.util.HashMap.hash(HashMap.java:340) ~[na:na]
at java.base/java.util.HashMap.put(HashMap.java:608) ~[na:na]
at java.base/java.util.HashSet.add(HashSet.java:220) ~[na:na]
at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:352) ~[na:na]
at org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:355) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:239) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:224) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:198) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl.endLoading(CollectionReferenceInitializerImpl.java:154) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishLoadingCollections(AbstractRowReader.java:232) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:190) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:96) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:105) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:705) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2203) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:595) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264) ~[hibernate-core-5.6.3.Final.jar:5.6.3.Final]