Hibernate Reactive 中的 LazyInitializationException 与 Quarkus (Panache)

E. *_* Dn 3 hibernate quarkus quarkus-panache hibernate-reactive

我有三个实体:采访、问答。面试可以有很多问题(反之亦然,多对多),问题可以有很多答案(一对多)。

我通过以下方式持久化和获取实体:

@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
Run Code Online (Sandbox Code Playgroud)

所有持久性操作都工作正常,采访和问题都创建得很好,然后也都取得了很好的效果。但是当我创建答案(也很好)然后尝试 findById Question 或 Interview 实体时,我收到以下错误(此错误用于获取问题):

"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"
Run Code Online (Sandbox Code Playgroud)

它曾经在 findById (面试)中显示相同的错误,但 FetchMode.JOIN 解决了​​这个问题。由于某种原因,FetchMode.JOIN 在获取答案时被忽略(?)。我没有使用 findById,还尝试使用 left join fetch 手动编写 HQL,但得到了相同的结果。我在这里缺少什么?

采访单位:

"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"
Run Code Online (Sandbox Code Playgroud)

问题实体:

@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(),
)
Run Code Online (Sandbox Code Playgroud)

回答实体:

@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(),
)
Run Code Online (Sandbox Code Playgroud)

Dav*_*lto 5

在 Hibernate Reactive 中,必须显式获取惰性集合。如果您尝试访问惰性关联而不先获取它,您将看到该错误。

以下是获取与 Panache 关联的方法:

import org.hibernate.reactive.mutiny.Mutiny;

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

请注意,以这种方式获取关联将导致对数据库进行额外的查询。根据用例,通过查询中的联接获取来急切地加载关联可能会更有效。这也应该有效:

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

在这种情况下,Hibernate Reactive 将通过单个查询加载所有内容。

此外,在使用 Hibernate 时,开发人员有责任保持双向关联在双方上同步