Hibernate原生查询性能低下,转换为JPQL语句报错如何解决
一、性能问题猜测验证
你的猜测部分正确,原生查询本身不会直接导致性能大幅低于直接执行SQL,Hibernate执行原生查询偏慢的核心原因和JPQL的优势如下:
- 原生查询默认无法直接复用Hibernate的二级缓存、查询缓存,而符合规范的JPQL可以直接配置缓存,重复查询时性能提升明显
- JPQL支持自动生成适配数据库的count查询,你手写的count查询如果存在索引未命中、逻辑冗余,也会拖慢整体分页查询速度
- 排除缓存因素外,如果原生查询执行速度仍远低于SQL工具,建议检查是否存在参数类型映射错误、Hibernate结果集转换额外开销的问题
二、报错JPQL的问题定位
你写的JPQL报语法错误的核心原因有两个:
- JPQL不支持无关联实体的直接JOIN,你直接
JOIN TwitterList twitterList和上游的TwitterPost没有关联属性绑定,会生成非法的笛卡尔积查询 - ON子句中不能直接使用IN判断集合归属,JPQL有专门的
MEMBER OF语法用于判断元素是否属于实体的集合属性
三、正确JPQL改写方案
首先确认实体已配置正确的关联关系:
TwitterList实体存在@ManyToMany注解修饰的accountsTrackedByLists属性,关联TwitterAccount实体TwitterPost实体存在@ManyToOne注解修饰的author属性(关联TwitterAccount)、以及@ManyToMany注解修饰的twitterUsersHidingPosts属性(关联隐藏帖子的用户账号)
改写后的可用JPQL如下:
@Query( value = "SELECT DISTINCT twitterPost " + "FROM TwitterList twitterList " + // 直接通过关联属性JOIN,避免无关联JOIN的语法错误 "JOIN twitterList.accountsTrackedByLists tweetAuthorFromList " + "JOIN TwitterPost twitterPost ON twitterPost.author = tweetAuthorFromList " + "WHERE twitterList.owner.id = :twitterAccountId " + // 使用MEMBER OF语法判断用户是否在隐藏列表中,等价于原生SQL的LEFT JOIN判空 "AND :twitterAccountId NOT MEMBER OF twitterPost.twitterUsersHidingPosts" ) Page<TwitterPost> findAllNonHiddenPostsFromListsForTwitterAccountId(Long twitterAccountId, Pageable pageable);
如果需要进一步提升查询性能,可以添加查询缓存配置:
import org.hibernate.jpa.QueryHints; import org.springframework.data.jpa.repository.QueryHints; @QueryHints(value = @QueryHint(name = QueryHints.HINT_CACHEABLE, value = "true")) @Query( // 上述JPQL内容 )
内容的提问来源于stack exchange,提问作者Borzi
相关产品推荐
相关产品推荐

