在不调用属性的情况下获取休眠惰性关系 n+1 - Kotlin

Posted

技术标签:

【中文标题】在不调用属性的情况下获取休眠惰性关系 n+1 - Kotlin【英文标题】:Hibernate lazy relations are fetched n+1 without properties being called - Kotlin 【发布时间】:2019-03-14 08:42:53 【问题描述】:

我正在使用休眠并执行一个简单的查询来选择所有给定的实体。我只想要实体而不是它的关系。然而,hibernate 确实以 n+1 的方式获取关系和关系的关系。

这是一个 Spring Boot 应用程序,用 Kotlin 编写。

关系用 FetchType.LAZY 注释,我正在做的查询看起来像这样:

entityManager.createQuery("SELECT a FROM Apartment a")
    .resultList

这是我调用的唯一代码,我什至不从控制器返回结果,所以它不是调用惰性属性的序列化库。

这些是实体:

@Entity
class Apartment(
        @Id
        private val id: String,
        private val heading: String,
        @ManyToOne(fetch = LAZY)
        private val building: Building
)

@Entity
class Building(
        @Id
        private val id: String,
        private val heading: String,
        @ManyToOne(fetch = FetchType.LAZY)
        private val owner: User
)

@Entity
@Table(name="users")
class User(
        @Id
        private val id: String,
        private val email: String
)

我打开了休眠查询日志,当我运行上面的查询时,它看起来像:

Fetch all apartments
Fetch buildings for apartment 1
Fetch users for building 1
fetch buildings for apartment 2
fetch users for building 2
....

我已经创建了一个重现问题的 git 存储库:https://github.com/Herlevsen/hibernate-lazy-fetch-repoduction

它带有一个启动 postgres 数据库的 docker-compose 文件,应用程序会自动创建模式并在启动时创建一些虚拟数据。因此,启动和运行相当容易。

我真的希望有人能告诉我发生了什么事。

谢谢!

【问题讨论】:

【参考方案1】:

您必须open 实体类。 Hibernate 需要 open 类来创建代理以支持 LAZY - ManyToOne 字段:

@Entity
open class Apartment(
    @Id
    private val id: String,
    private val heading: String,
    @ManyToOne(fetch = LAZY)
    private val building: Building
)

@Entity
open class Building(
    @Id
    private val id: String,
    private val heading: String,
    @ManyToOne(fetch = FetchType.LAZY)
    private val owner: User
)

@Entity
@Table(name="users")
open class User(
    @Id
    private val id: String,
    private val email: String
)

您也可以使用all-open-Plugin。

【讨论】:

谢谢,这就是解决方案 :-) 我尝试使用全开放插件并使用 Hibernates @Entity 注释对其进行配置。但是,我的代码无法编译,因为我对我的大部分属性都使用了私有设置器。你知道有没有办法解决这个问题? 能否提供带有私有设置器和编译错误消息的源代码? 我已经更新了存储库,特别是 Apartment 类。现在的错误如下: ...../demo/src/main/kotlin/com/example/demo/Apartment.kt: (17, 17): Private setter is not allowed for open properties。这个错误对我来说确实有意义。但是,当不应该从外部改变属性时,我想将我的设置器保密。因此,如果可以通过插件在构建时从设置器中删除私有。这样我自己的代码不能调用setter,但是hibernate仍然可以代理setter。你听说过这样的插件吗? :-) 谢谢! 您可以使用internal 代替privateinternal 表示,它在同一个模块/文件(kotlinlang.org/docs/reference/visibility-modifiers.html)中随处可见。只要将每个实体放在一个文件中,就和private一样。

以上是关于在不调用属性的情况下获取休眠惰性关系 n+1 - Kotlin的主要内容,如果未能解决你的问题,请参考以下文章

核心数据 - 在不触发错误的情况下获取对多关系的 objectID

在不休眠的情况下使用 VBScript 压缩文件

如何在不调用索引函数或任何内置函数的情况下获取列表中的索引?

While(1) 循环在不休眠的情况下减少 CPU

避免对未获取的惰性对象进行 Jackson 序列化

你能在不调用 org.freedesktop.DBus.Properties.Get 的情况下检索 D-Bus 属性吗?