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查询,可以通过以下方式:
- 修改
@ManyToOne注解为立即加载:@ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "shelter_id") val shelter: Shelter - 自定义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.
相关产品推荐
相关产品推荐

