如何优化JPQL查询,仅获取d1.disable=false的EntityC关联数据?
问题与解决方案
实体结构
EntityA { @ManyToOne(fetch = LAZY) EntityB b1; @ManyToOne(fetch = LAZY) EntityB b2; } EntityB { @OneToMany(fetch = LAZY, cascade=ALL) List<EntityC> c1; } EntityC { @ManyToOne(fetch = LAZY) EntityD d1; } EntityD { Boolean disable; }
现有HQL查询
@Query(value = """ select a from EntityA a join fetch a.b1 ab1 join fetch a.b2 ab2 """)
需求
调试发现d1为HibernateProxy对象,需优化查询,仅获取关联d1.disable = false的EntityC实体,同时初始化d1避免代理对象。
优化方案
方案1:逐层关联抓取+过滤条件
直接通过多层join fetch初始化所有关联对象,并添加过滤条件:
@Query(value = """ select distinct a from EntityA a join fetch a.b1 ab1 join fetch ab1.c1 ac1 join fetch ac1.d1 ad1 join fetch a.b2 ab2 join fetch ab2.c1 ac2 join fetch ac2.d1 ad2 where ad1.disable = false and ad2.disable = false """)
- 作用:通过
join fetch逐层初始化EntityA -> b1 -> c1 -> d1和EntityA -> b2 -> c1 -> d1的关联链,彻底避免HibernateProxy;同时通过where条件过滤掉d1.disable = true的EntityC实例。 - 注意:由于集合关联会产生笛卡尔积,添加
distinct关键字去除重复的EntityA实例。
方案2:Exists子查询+批量抓取(避免笛卡尔积)
若担心笛卡尔积导致性能问题,可采用Exists子句过滤数据,配合批量抓取注解避免N+1查询:
优化后的HQL
@Query(value = """ select distinct a from EntityA a join fetch a.b1 ab1 join fetch a.b2 ab2 where exists ( select 1 from ab1.c1 ac1 where ac1.d1.disable = false ) and exists ( select 1 from ab2.c1 ac2 where ac2.d1.disable = false ) """)
实体类添加批量抓取注解
在EntityB的c1字段和EntityC的d1字段上添加@BatchSize注解:
// EntityB类中 @OneToMany(fetch = LAZY, cascade=ALL) @BatchSize(size = 10) // 批量抓取数量可根据业务调整 List<EntityC> c1; // EntityC类中 @ManyToOne(fetch = LAZY) @BatchSize(size = 10) EntityD d1;
- 作用:Exists子句仅过滤出关联的b1、b2中存在符合条件的c1的EntityA;
join fetch初始化b1和b2;@BatchSize让Hibernate批量抓取c1和d1集合,避免N+1查询,同时确保d1被初始化而非代理对象。
内容的提问来源于stack exchange,提问作者Krazim00da
相关产品推荐
相关产品推荐

