You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate Statistics未正确统计queryExecutionCount问题求助

提问背景

我在实体的多对一关系中构造了N+1问题,并编写测试用例验证该问题。运行测试时,日志显示共执行了3条查询,但Hibernate Statistics返回的queryExecutionCount却为1,这显然不符合预期。我已在测试中执行了flush和clear EntityManager操作以避免缓存问题。

测试日志

Hibernate: insert into shelter (name,id) values (?,default)
Hibernate: insert into shelter (name,id) values (?,default)
Hibernate: insert into messages (name,shelter_id,id) values (?,?,default)
Hibernate: insert into messages (name,shelter_id,id) values (?,?,default)
Hibernate: insert into messages (name,shelter_id,id) values (?,?,default)
Hibernate: select a1_0.id,a1_0.name,a1_0.shelter_id from messages a1_0
Hibernate: select s1_0.id,s1_0.name from shelter s1_0 where s1_0.id=?
Hibernate: select s1_0.id,s1_0.name from shelter s1_0 where s1_0.id=?
Hibernate: drop table if exists messages cascade

相关代码

实体类Animal

@Entity
@Table(name = "messages")
data class Animal(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,

    @Column(nullable = false)
    val name: String,

    @ManyToOne
    @JoinColumn(name = "shelter_id")
    val shelter: Shelter
)

实体类Shelter

@Entity
@Table(name = "shelter")
data class Shelter(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,

    @Column(nullable = false)
    val name: String,
)

Repository

@Repository
interface AnimalRepository: JpaRepository<Animal, Long> {}

测试类

@DataJpaTest
class AnimalRepositoryTest @Autowired constructor(
    private val animalRepository: AnimalRepository,
    private val entityManager: EntityManager
) {
    private lateinit var hibernateStatistics: Statistics

    @BeforeEach
    fun setUp() {
        val sessionFactory = entityManager.unwrap(Session::class.java).sessionFactory
        hibernateStatistics = sessionFactory.statistics
        hibernateStatistics.isStatisticsEnabled = true
        hibernateStatistics.clear()  // Reset statistics before each test

        // Insert initial test data
        val shelter = Shelter(name = "Happy Shelter")
        val shelter2 = Shelter(name = "Happy Shelter2")
        entityManager.persist(shelter)
        entityManager.persist(shelter2)

        val animal1 = Animal(name = "Lion", shelter = shelter)
        val animal2 = Animal(name = "Tiger", shelter = shelter)
        val animal3 = Animal(name = "Panda", shelter = shelter2)
        entityManager.persist(animal1)
        entityManager.persist(animal2)
        entityManager.persist(animal3)

        entityManager.flush()
        entityManager.clear()  // Clear persistence context to ensure fresh retrieval
    }

    @Test
    @Transactional
    @Rollback
    fun `should execute one SQL query when finding all animals with shelter`() {
        // Perform the operation (fetch all animals with shelter)
        val animals = animalRepository.findAll()

        // Assert the result
        assertThat(animals).hasSize(3)
        animals.forEach { animal -> animal.shelter.name}

        // Check Hibernate statistics for SQL query count
        val executedQueryCount = hibernateStatistics.queryExecutionCount
        assertThat(executedQueryCount).isEqualTo(1)
    }
}

问题分析与解决方案

核心原因

你使用的queryExecutionCount统计指标范围不符合预期:

  • queryExecutionCount仅统计JPQL/HQL查询的执行次数,这里只有animalRepository.findAll()对应的1次JPQL查询,所以返回1
  • 日志中那2次查询Shelter的SQL是懒加载触发的原生SQL查询,这类查询不会被计入queryExecutionCount,而是被统计在懒加载相关的指标中

修正方案

1. 替换为正确的统计指标

根据你的测试目标选择对应指标:

  • 统计所有执行的SQL语句总数(包括懒加载):使用sqlExecutionCount
  • 统计懒加载触发的查询次数:使用lazyFetchCount

2. 修改测试断言

将测试中的断言代码替换为:

// 验证总SQL执行数:1次Animal查询 + 2次Shelter懒加载查询 = 3
val totalSqlCount = hibernateStatistics.sqlExecutionCount
assertThat(totalSqlCount).isEqualTo(3)

// 可选:单独验证懒加载次数
val lazyFetchCount = hibernateStatistics.lazyFetchCount
assertThat(lazyFetchCount).isEqualTo(2)

额外优化:解决N+1问题

如果你想彻底消除N+1查询,可以通过以下方式:

  1. 修改@ManyToOne注解为立即加载:
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "shelter_id")
    val shelter: Shelter
    
  2. 自定义Repository方法,使用JOIN FETCH提前关联查询:
    @Repository
    interface AnimalRepository : JpaRepository<Animal, Long> {
        @Query("SELECT a FROM Animal a JOIN FETCH a.shelter")
        fun findAllWithShelter(): List<Animal>
    }
    
    调用findAllWithShelter()时,只会执行1条SQL,此时queryExecutionCount和sqlExecutionCount都会返回1,懒加载次数为0。

内容的提问来源于stack exchange,提问作者Andrin F.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:54:50