LazyInitializationException в Hibernate Reactive с Quarkus (Panache)

У меня есть три объекта: интервью, вопрос и ответ. Интервью может иметь много Вопросов (и наоборот, многие-ко-многим), Вопрос может иметь много Ответов (один-ко-многим).

Я сохраняю и извлекаю сущности следующим образом:

@ApplicationScoped
internal class InterviewRepository : PanacheRepository<Interview> {
    fun persistInterview(interview: Interview): Uni<Interview> {
        return Panache.withTransaction {
            persist(interview)
        }
    }

    fun getInterview(interviewId: Long): Uni<Interview> {
        return findById(interviewId)
    }
}
// same repos for Question and Answer

Все операции сохранения работают нормально, интервью и вопросы создаются нормально, а затем и то, и другое нормально извлекается. Но когда я создаю ответ (тоже хорошо), а затем пытаюсь найти объекты вопроса или интервью findById, я получаю следующую ошибку (это для получения вопроса):

"org.hibernate.HibernateException: java.util.concurrent.CompletionException: org.hibernate.LazyInitializationException: HR000056: Collection cannot be initialized: com.my.company.question.Question.answers - Fetch the collection using 'Mutiny.fetch', 'Stage.fetch', or 'fetch join' in HQL"

Раньше показывалась та же ошибка для findById (интервью), но FetchMode.JOIN решил проблему. По какой-то причине FetchMode.JOIN игнорируется при получении ответа (?). Вместо использования findById я также попытался вручную написать HQL, используя левое соединение, но получил тот же результат. Что мне здесь не хватает?

Объект интервью:

@Entity(name = "interview")
@RegisterForReflection
internal data class Interview (
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    val interviewId: Long? = null,

    @Column(name = "client_id", nullable = false)
    val clientId: Long = Long.MIN_VALUE,

    @ManyToMany(mappedBy = "interviews", fetch = FetchType.LAZY)
    @Fetch(FetchMode.JOIN)
    val questions: MutableSet<Question> = mutableSetOf(),
)

Сущность вопроса:

@Entity(name = "question")
@RegisterForReflection
internal data class Question (
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
        val questionId: Long? = null,

        @Column(name = "client_id", nullable = true)
        val clientId: Long? = null,

        @OneToMany(mappedBy = "question", fetch = FetchType.LAZY)
        @OnDelete(action = CASCADE)
        @Fetch(FetchMode.JOIN)
        val answers: MutableSet<Answer> = mutableSetOf(),

        @ManyToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
        @JoinTable(
                name = INTERVIEW_QUESTION_TABLE,
                joinColumns = [JoinColumn(name = "question_id", referencedColumnName = "id")],
                inverseJoinColumns = [JoinColumn(name = "interview_id", referencedColumnName = "id")]
        )
        @Fetch(FetchMode.JOIN)
        @JsonIgnore
        val interviews: MutableList<Interview> = mutableListOf(),
)

Ответить Сущность:

@Entity(name = "answer")
@RegisterForReflection
internal data class Answer (
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    val answerId: Long? = null,

    @Column(name = "question_id", nullable = false)
    val questionId: Long = Long.MIN_VALUE,

    @ManyToOne(fetch = FetchType.LAZY)
    @Fetch(FetchMode.JOIN)
    @JoinColumn(
        name = "question_id",
        foreignKey = ForeignKey(ConstraintMode.NO_CONSTRAINT),
        nullable = false,
        insertable = false,
        updatable = false,
    )
    @JsonIgnore
    val question: Question = Question(),
)
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
200
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В Hibernate Reactive ленивые коллекции должны извлекаться явно. Если вы попытаетесь получить доступ к ленивой ассоциации, не извлекая ее сначала, вы увидите эту ошибку.

Вот как вы можете получить ассоциацию с Panache:

import org.hibernate.reactive.mutiny.Mutiny;

    fun getInterview(interviewId: Long): Uni<Interview> {
        return findById(interviewId)
            .call(interview -> Mutiny.fetch(interview.questions))
    }

Обратите внимание, что получение ассоциаций таким образом вызовет дополнительный запрос к базе данных. В зависимости от варианта использования может быть эффективнее загружать ассоциацию сразу с выборкой соединения в запросе. Это также должно работать:

    fun getInterview(interviewId: Long): Uni<Interview> {
        return find("from Interview i left join fetch i.questions where i.id = :id", Parameters.with("id", interviewId))
    }

В этом случае Hibernate Reactive загрузит все одним запросом.

Кроме того, при использовании Hibernate разработчик несет ответственность за синхронизацию двунаправленных ассоциаций с обеих сторон.

Я пробовал оба варианта, которые вы предложили, но получаю ту же ошибку, как только в «Интервью» появляются связанные вопросы (т. е. до того, как я вставлю вопрос, он работает нормально, но после вставки и повторного извлечения выдает ошибку выше). Я создал демо на своем GitHub, посмотрите, пожалуйста, если возможно (я выложил ридми с инструкциями по воспроизведению): github.com/DnEgorWeb/ogram-interviews

E. Dn 08.11.2022 16:23

Спасибо за репродуктор. Это действительно полезно. Но можете ли вы создать тестовый пример, который я могу запустить, пожалуйста? Я не знаком с почтальоном, и мне потребуется некоторое время, чтобы понять, как воспроизвести ошибку. Последовательность запросов curl к остальным API тоже будет хорошей.

Davide D'Alto 08.11.2022 16:32

Спасибо за проверку! Я только что обновил репо с созданным тестовым примером.

E. Dn 08.11.2022 18:22

Спасибо, я посмотрел, и я думаю, что это может быть ошибка. Мне нужно создать более простой тестовый пример (тот, который использует только Hibernate Reactive), чтобы убедиться в этом.

Davide D'Alto 08.11.2022 22:54

Хорошо, я разветвил проект и внес некоторые изменения. Теперь вроде работает: github.com/DavideD/ogram-interviews/commits/master

Davide D'Alto 09.11.2022 22:29

Я отправил pr в ваш репозиторий с исправлениями, которые должны заставить все работать. В конечном счете, я думаю, что ответ на ваш вопрос был правильным, но есть еще некоторые вещи, которые нужно исправить в проекте.

Davide D'Alto 09.11.2022 22:38

Здравствуйте, Давиде, я не могу отблагодарить вас за исправления и комментарии. Я обязательно проверю ссылки, которые вы предоставили по этому вопросу. По какой-то причине теперь при сохранении ответа происходит сбой с ошибкой (хотя тесты в порядке): Невозможно добавить или обновить дочернюю строку: сбой ограничения внешнего ключа (interviews.answer, CONSTRAINT FK8frr4bcabmmeyyu60qt7iiblo FOREIGN KEY (question_id) REFERENCES question (id) ON DELETE CASCADE)". Я пытаюсь понять, почему

E. Dn 10.11.2022 09:23

Я не уверен в этом, но обычно это зависит от порядка удаления объектов. Я бы попытался удалить связанную ассоциацию, прежде чем удалять вовлеченный объект. Вероятно, такие вопросы лучше задавать в пользовательской теме Hibernate на Zulip: hibernate.zulipchat.com/#narrow/stream/132096-hibernate-user

Davide D'Alto 10.11.2022 10:20

Я пробовал разные способы, но все еще имею эту проблему с выборкой (случай «один ко многим», «многие ко многим» работает нормально). Я создал намного меньшую и более простую демонстрацию, если у вас еще есть время (и терпение...), пожалуйста, посмотрите: stackoverflow.com/questions/74391462/…

E. Dn 10.11.2022 16:27

Ммм... Я начал смотреть на него и не вижу ничего плохого. Я не знаю, почему у вас возникает ошибка ленивой инициализации. Я не вижу никакой ошибки. Мне придется создать проект без kotlin и проверить, не повторится ли та же ошибка.

Davide D'Alto 10.11.2022 18:48

На вашем месте я бы создал проблему на github.com/quarkusio/quarkus/issues - я не думаю, что SO подходит для таких проблем.

Davide D'Alto 10.11.2022 19:47

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