Spring Data JPA:添加分页后Projection功能失效问题解决
嘿,我之前也碰到过一模一样的问题!这不是Spring Data的Bug,而是Spring Boot 1.5.x配套的Spring Data JPA版本在处理自定义DTO构造函数查询的分页时,自动生成count查询的逻辑有缺陷——它会错误地把count语句直接插在原SELECT和FROM之间,导致SQL语法彻底混乱,就像你看到的那样。
下面给你几个可行的解决办法,按推荐程度排序:
1. 手动指定count查询(最稳妥,不改动现有DTO结构)
直接在@Query注解里用countQuery参数单独编写统计总数的查询,绕开框架自动生成的错误count语句:
@Query( value = "SELECT DISTINCT new com.mycompany.dto.MyDto(me.property1, me.property2, ...) FROM MyEntiry me...", // 注意:count查询要和主查询的过滤条件完全一致,比如主查询有WHERE子句,这里也要同步添加 countQuery = "SELECT COUNT(DISTINCT me) FROM MyEntiry me..." ) Page<MyDto> findEntities(Pageable pageable);
这个方法的好处是不用修改你现有的DTO结构,只要保证count查询的过滤逻辑和主查询匹配,就能得到准确的分页总数。
2. 改用接口投影(如果业务场景允许)
如果你的DTO只是简单映射实体属性,没有复杂的构造逻辑(比如属性转换、多属性组合),可以换成接口投影的方式,Spring Data 1.5.x对这种方式的分页支持更友好:
先定义一个投影接口,方法名要和实体的属性getter对应:
public interface MyProjection { String getProperty1(); String getProperty2(); // 其他需要的属性对应的get方法 }
然后修改查询方法:
@Query("SELECT DISTINCT me FROM MyEntiry me...") Page<MyProjection> findEntities(Pageable pageable);
这种方式下,框架能正确生成主查询和count查询,不会出现语法错误。但如果你的DTO需要做复杂的属性处理,这个方法就不适用了。
3. 升级Spring Boot版本(彻底解决问题,但要注意兼容性)
如果项目允许的话,升级到Spring Boot 2.x及以上版本,这个问题在后续版本的Spring Data JPA里已经被修复了——框架会正确识别构造函数投影的语法结构,生成符合规范的count查询。不过升级前要仔细检查兼容性,比如Hibernate版本、其他第三方依赖是否适配新的Spring Boot版本。
问题根源说明
在Spring Data JPA 1.5.x版本中,处理返回自定义DTO(通过构造函数)的分页查询时,框架生成count查询的逻辑没有正确解析构造函数投影的语法,错误地将select count(...)直接插入到原SELECT子句之后,导致最终的HQL变成了SELECT DISTINCT new ... select count(...) FROM ...,这完全不符合HQL的语法规范,所以才会抛出QuerySyntaxException。
内容的提问来源于stack exchange,提问作者Sasha Shpota

