Реактивное размах: прекратите выборку двунаправленного связанного объекта с помощью Mutiny fetch

Пример переполнения стека при получении объекта с использованием реактивного размаха Java Quarkus:

      @Table(name = "author")
public class Author extends PanacheEntityBase {
private Long id;
private String authorName;
@OneToMany(fetch = FetchType.LAZY)
private Set<Book> books;
}

---------
@Table(name = "book")
public class Book extends PanacheEntityBase {
private Long id;
private String bookName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id")
private Author author;
@ManyToMany(fetch = FetchType.LAZY)
private Set<Publisher> publishers;
}

---------
@Table(name = "publisher")
public class Publisher extends PanacheEntityBase {
private Long id;
private String publisherName;
@ManyToMany(fetch = FetchType.LAZY)
@JoinColumn(...)
private Set<Book> books;
}

Я делаюfindавтор и получил ошибку с LazyInitializationException.

return find("authorId", id).firstResult()
.onItem().call(au -> Mutiny.fetch(au.books())); Основная причина: двунаправленные отношения между авторами и издателями могут привести к бесконечному циклу связанных объектов.

Конечно, если я сопоставлю его с объектом DTO без каких-либо связей, проблема будет решена. Ссылка по ссылке: https://github.com/quarkusio/quarkus/issues/23757 Проблема в том, что я хочу продолжать что-то работать с Author, а не с AuthorDTO...

1 ответ

В вашем проекте у вас есть несколько ассоциаций:

  • Двунаправленная связь «один ко многим» между автором и книгой.
  • Однонаправленная связь «многие-ко-многим» между книгой и издателем.

Первая проблема заключается в том, что вы не сообщаете Джексону, что связь между Автором и Книгой является двунаправленной, это может привести к исключению StackOverflow . Вы можете решить это, используя аннотации@JsonManagedReferenceи@JsonBackReference:

      class Author {

    ...

    @JsonManagedReference
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "author")
    public Set<Book> books = new HashSet<>();
}

class Book {

    ...
    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id")
    public Author author;

}

The LazyInitializationExceptionпроисходит потому, что вы получаетеbooksассоциация, но неpublishersодин. Также, как правило, вам не следует использоватьMutiny.fetchчтобы вызвать ассоциации. Проблема в том, что в конечном итоге вы столкнетесь с проблемой N+1 . Вы можете решить проблему, используя HQL-запрос:

      
@ApplicationScoped
public class AuthorRepo implements PanacheRepository<Author> {

    public Uni<Author> findAndFetch(Long id) {
        return find( "select a from Author a left join fetch a.books b left join fetch b.publishers where a.id = ?1", id )
                .firstResult();
    }

    ...
}

Таким образом, вы можете получить все, что вам нужно, с помощью одного запроса.

Я разветвил ваш проект и добавил в этот коммит все изменения, необходимые для его быстрой работы .

Я не проверял это, но обозначил связь с издателями как SUBSELECTtype, также должен хорошо работать в этом сценарии. Таким образом, вам просто нужно получить первую ассоциацию (Mutiny.fetch(book.publishers)), и будут выбраны все издатели каждой книги.

Другие вопросы по тегам