升级Spring Boot3后,JPA+Hibernate在PostgreSQL9.6.3分页查询报错
升级Spring Boot 3后,执行findTop1ByName这类Top查询或带分页的JPA方法时,会触发SQL语法错误。原因是Spring Boot 3默认集成的Hibernate 6.x生成了fetch first ? rows only的SQL语句,而PostgreSQL 9.6不支持参数化的fetch子句(仅接受常量值),最终报错:
ERROR: syntax error at or near "$2"
以下是几种可行的解决方案:
方案一:自定义PostgreSQL方言,强制使用limit语法
创建自定义方言类,继承PostgreSQL95Dialect,重写分页相关逻辑,让Hibernate生成PostgreSQL 9.6支持的limit/offset语法:
import org.hibernate.dialect.PostgreSQL95Dialect; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.tree.select.QuerySpec; public class PostgreSQL96CustomDialect extends PostgreSQL95Dialect { @Override public void appendFetchOffsetClause(QuerySpec querySpec, SqlAppender sqlAppender) { if (querySpec.getOffset() != null) { sqlAppender.append(" limit ? offset ?"); } else { sqlAppender.append(" limit ?"); } } @Override public boolean supportsFetchClause() { return false; } }
在配置文件中指定该方言:
# application.properties spring.jpa.properties.hibernate.dialect=你的项目包路径.PostgreSQL96CustomDialect
方案二:手动编写JPQL指定分页逻辑
针对单个报错的Repository方法,使用@Query注解直接编写JPQL,显式使用limit语法:
@Repository public interface CampaignStatusDao extends JpaRepository<CampaignStatus, Long> { @Query("select cs from CampaignStatus cs where cs.name = ?1 order by cs.campaignStatusId asc limit 1") CampaignStatus findTop1ByName(String status); }
分页查询时,也可通过类似方式手动构造包含limit和offset的JPQL语句。
方案三:降级Hibernate版本(不推荐)
将Hibernate版本降级到5.6.x系列(该版本对PostgreSQL 9.6的分页语法兼容性更好),在Maven的pom.xml中添加:
<dependency> <groupId>org.hibernate.orm</groupId> <artifactId>hibernate-core</artifactId> <version>5.6.15.Final</version> </dependency>
注意:降级可能引发Spring Boot 3其他组件的兼容性问题,需全面测试后再使用。
原理说明
Spring Boot 3默认集成的Hibernate 6.x针对PostgreSQL 10+优化了分页语法,使用fetch first ? rows only,但PostgreSQL 9.6仅支持常量值的fetch子句,而参数化的limit ?语法是兼容的,因此通过修改方言强制生成limit语法即可解决问题。
内容的提问来源于stack exchange,提问作者Egor

