When there are multiple many-to-many relations with {{FetchMode.JOIN}}, {{session.find}} returns a result with a child relation of type {{List}} containing duplicates.
To avoid {{MultipleBagFetchException: cannot simultaneously fetch multiple bags}} when multiple {{@ManyToMany}} collections have {{@Fetch(FetchMode.JOIN)}}, only one collection has type {{List}} and others have type {{Set}}.
{code:java}@Entity @Data public class Book implements Serializable {
@Id @GeneratedValue private Long id;
private String isbn;
private String title; private LocalDate publicationDate;
@ManyToMany @Fetch(FetchMode.JOIN) private List<Author> authors = new ArrayList<>();
@ManyToMany @Fetch(FetchMode.JOIN) private Set<Category> categories = new LinkedHashSet<>(); }{code}
With the following test data
{code:java}softwareDevelopment = new Category("Software development"); session.save(softwareDevelopment);
systemDesign = new Category("System design"); session.save(systemDesign);
martinFowler = new Author("Martin Fowler"); session.save(martinFowler);
gregorHohpe = new Author("Gregor Hohpe"); session.save(gregorHohpe);
gregorHohpe = new Author(); gregorHohpe.setFullName("Gregor Hohpe"); session.save(gregorHohpe);
bobbyWoolf = new Author(); bobbyWoolf.setFullName("Bobby Woolf"); session.save(bobbyWoolf);
poeaa = new Book(); poeaa.setIsbn("007-6092019909"); poeaa.setTitle("Patterns of Enterprise Application Architecture"); poeaa.setPublicationDate(LocalDate.parse("2002-11-15")); poeaa.getAuthors().addAll(List.of(martinFowler)); poeaa.getCategories().addAll(List.of(softwareDevelopment, systemDesign)); session.save(poeaa);
eip = new Book(); eip.setIsbn("978-0321200686"); eip.setTitle("Enterprise Integration Patterns"); eip.setPublicationDate(LocalDate.parse("2003-10-20")); eip.getAuthors().addAll(List.of(gregorHohpe, bobbyWoolf)); eip.getCategories().addAll(List.of(softwareDevelopment, systemDesign)); session.save(eip);{code}
a Book entity found by ID contains duplicates in {{List<Author> authors}}
{code:java}@Test void testFindByIdNoTrans() { try (Session session = HibernateUtil.getSessionFactory().openSession()) { Book poeaa = session.find(Book.class, this.poeaa.getId()); Book eip = session.find(Book.class, this.eip.getId());
assertThat(poeaa.getTitle()).isEqualTo(this.poeaa.getTitle()); assertThatHasAuthors(poeaa, martinFowler.getFullName());
assertThat(eip.getTitle()).isEqualTo(this.eip.getTitle()); assertThatHasAuthors(eip, gregorHohpe.getFullName(), bobbyWoolf.getFullName()); } }{code}
{code:java}List<Author> authors = ["Martin Fowler", "Martin Fowler"]{code}
Probably, because {{categories}} has size 2:
{code:java}Set<Category> categories = ["Software development", "System design"]{code}
When there are 2 authors and 2 categories there are no duplicates in child relations.
Beginning transaction with {{session.beginTransaction()}} doesn't have any effect on the duplicates.
See the test [ com https://github . com/evgeniy-khist/hibernate-fetch-mode-join/blob/master/src/test/java/com/ example . / hibernate . / BookFetchModeJoinWithSetTests .java |https://github.com/evgeniy-khist/hibernate-fetch-mode-join/blob/master/src/test/java/com/example/hibernate/BookFetchModeJoinWithSetTests.java |smart-link ].
Using Spring Data JPA and making the test transactional with {{@org.springframework.transaction.annotation.Transactional}} solves the problem with duplicates. Spring Boot version - 2.2.1.RELEASE, Spring Data JPA version - 2.2.1.RELEASE, Hibernate version - 5.4.8.Final.
See the test [https://github.com/evgeniy-khist/ hibernate spring - data-jpa- fetch-mode-join/blob/master/src/test/java/com/example/ hibernate spring / data/jpa/ BookFetchModeJoinWithSetTests.java|https://github.com/evgeniy-khist/ hibernate spring - data-jpa- fetch-mode-join/blob/master/src/test/java/com/example/ hibernate spring / data/jpa/ BookFetchModeJoinWithSetTests.java|smart-link] . |
|