当前位置: 首页 > 知识库问答 >
问题:

Spring JPA/Hibernate存储库findAll正在执行N+1个请求,而不是默认情况下Kotlin中的连接

郤令
2023-03-14

我正在使用Kotlin开发一个Spring JPA/Hibernate应用程序,我想找到一个实体中的所有元素。

下面是示例代码:

    @Entity
    data class A {
      @Id
      val id: Long,
    
      @Column
      val uuid: UUID,
    
      @Column
      val name: String
    }
    @Entity
    data class B {
      @Id
      val id: Long,
    
      ...
    
      @Fetch(FetchMode.JOIN)
      @ManyToOne
      @JoinColumn(name = "a_uuid", referencedColumnName = "uuid", insertable = false, updatable = false)
      val a: A
    }
    @Repository
    interface Repo<B> : CrudRepository<B, Long>
   ...
   repo.findAll() // <-- This triggers N+1 queries instead of making a JOIN
   ...

共有1个答案

耿建弼
2023-03-14

您的另一个选择是使用EntityGraph。它允许通过分组我们想要检索的相关持久性字段来定义模板,并允许我们在运行时选择图类型。

这是一个通过修改代码而生成的示例代码。


@Entity
data class A (
    @Id
    val id: Long,

    @Column
    val uuid: UUID,

    @Column
    val name: String
) : Serializable

@NamedEntityGraph(
    name = "b_with_all_associations",
    includeAllAttributes = true
)
@Entity
data class B (
    @Id
    val id: Long,

    @ManyToOne
    @JoinColumn(name = "a_uuid", referencedColumnName = "uuid")
    val a: A
)

@Repository
interface ARepo: CrudRepository<A, Long>

@Repository
interface BRepo: CrudRepository<B, Long> {
    @EntityGraph(value = "b_with_all_associations", type = EntityGraph.EntityGraphType.FETCH)
    override fun findAll(): List<B>
}

@Service
class Main(
    private val aRepo: ARepo,
    private val bRepo: BRepo
) : CommandLineRunner {
    override fun run(vararg args: String?) {
        (1..3L).forEach {
            val a = aRepo.save(A(id = it, uuid = UUID.randomUUID(), name = "Name-$it"))
            bRepo.save(B(id = it + 100, a = a))
        }

        println("===============================================")
        println("===============================================")
        println("===============================================")
        println("===============================================")

        bRepo.findAll()
    }
}

B实体上定义了一个名为“B_with_all_associations”的实体图,并将其应用于load类型的B实体的存储库的findall方法。

    select
        b0_.id as id1_1_0_,
        a1_.id as id1_0_1_,
        b0_.a_uuid as a_uuid2_1_0_,
        a1_.name as name2_0_1_,
        a1_.uuid as uuid3_0_1_ 
    from
        b b0_ 
    left outer join
        a a1_ 
            on b0_.a_uuid=a1_.uuid

PS2.当您希望使用join解决N+1问题时,EntityGraph可以很好地回答您的问题。但我会推荐更好的解决方案:尝试用惰性加载来解决它。

PS3.使用非PK关联进行Hibernate不是一个好主意。我真的同意这个意见。我认为这是一个尚未解决的bug。它打破了Hibernate的惰性加载机制。

 类似资料:
  • 问题内容: 我有以下查询,希望在单个选择请求中运行: 问题在于,所有内容都是通过单独的多个查询获取的。我只希望在单个请求中获取团队和团队的球员以及每个球员的技能。但是取而代之的是,我有多个选择查询来获取每个球队,每个球员,每个球员的统计数据和技能。 以下是带有给定注释的实体: 游戏实体: 团队实体: 玩家实体: 您能指出犯下的错误吗?我需要一个选择查询来加载游戏,它是团队,团队的球员和每个球员的技

  • 默认情况下,Android Pie会要求应用程序使用HTTPS连接而不是HTTP。因此无法在HTTP中命中restful API

  • 问题内容: 我将实体添加到数据库中,并且工作正常。但是,当我检索列表时,会得到旧实体,直到取消取消部署应用程序并再次重新部署它之后,才会显示添加的新实体。这意味着默认情况下会缓存我的实体吗?但是,我没有在persistence.xml或任何此类文件中进行任何用于缓存实体的设置。 我什至尝试调用flush(),refresh()和merge()。但它仍然仅显示旧实体。我想念什么吗?请帮我。 问题答案

  • 我有一个关于Hibernate中乐观锁定的问题。我正试图深入乐观地锁定Hibernate,但我有一个疑问。Hibernate使用版本方法(整数或时间戳)来实现乐观锁定。要进行配置,可以使用@Version注释(或xml配置)并创建版本属性。另一个选项是在不使用乐观lock=“all”属性进行版本控制的情况下进行配置。 我的问题是,如果你没有定义任何版本属性,也没有指定乐观锁属性,在这种情况下,哪种

  • 在MySQL Ver 14.14 Distrib 5.7.25上,对于Linux(x86_64),默认设置似乎不起作用,因为我试图一行,其中包含父表键中不存在的值,而成功了。 为什么我必须,即使在默认情况下设置了? ...我将中,得到了这个... 因此,为了弄清楚为什么不应该成功,但却成功了,我阅读了以下网页... null 外键必须为。是。 默认存储引擎必须为。是的。 对每个外键声明使用。是。

  • 问题内容: 我有以下查询,希望在单个选择请求中运行: 问题在于,所有内容都是通过单独的多个查询获取的。我只希望在单个请求中获取团队和团队的球员以及每个球员的技能。但是取而代之的是,我有多个选择查询来获取每个球队,每个球员,每个球员的数据和技能。 以下是带有给定注释的实体: 游戏实体: 团队实体: 玩家实体: 您能指出犯下的错误吗?我需要一个选择查询来加载游戏,它是团队,团队的球员和每个球员的技能。