Реактивное размах: прекратите выборку двунаправленного связанного объекта с помощью 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();
}
...
}
Таким образом, вы можете получить все, что вам нужно, с помощью одного запроса.
Я разветвил ваш проект и добавил в этот коммит все изменения, необходимые для его быстрой работы .
Я не проверял это, но обозначил связь с издателями как
SUBSELECT
type, также должен хорошо работать в этом сценарии. Таким образом, вам просто нужно получить первую ассоциацию (Mutiny.fetch(book.publishers)
), и будут выбраны все издатели каждой книги.