Firebird Hibernate方言分页参数顺序错误问题求助
Firebird 2.5 + Hibernate 6.x 自定义原生查询分页时first/skip参数顺序错位问题
环境配置
- Firebird 2.5
- Hibernate 6.2 或 6.5
- Spring Data JPA(基于
PagingAndSortingRepository)
问题描述
调用自定义原生查询方法并传入page>0的Pageable对象时,Hibernate自动生成的SQL中skip和first参数顺序颠倒,导致Firebird抛出语法错误。但使用标准方法(如repository.findAll(paging))时,参数顺序是正确的。
代码示例
控制器代码
Pageable paging = PageRequest.of(1, 10); Page<MaterialBrand> items = repository.findByMaterialSizesQuery(25, paging);
仓库代码
@Query( value = "select t_m_brands.id...... where gmin <= :size and gmax >= :size", nativeQuery = true ) Page<MaterialBrand> findByMaterialSizesQuery(@Param("size") Float size, Pageable pageable);
错误现象
Hibernate生成的错误SQL:
select skip ? first ? t_m_brands.id,....
触发的错误:
内部服务器错误(状态码500),无法预编译语句[动态SQL错误;SQL错误码=-104;未知标记 - 第1行,第21列;?[SQLState:42000, ISC错误码:335544634]]
正确的SQL应该是:
select first ? skip ? t_m_brands.id,....
已尝试的无效方案
实现QueryRewriter接口重写查询,但此时查询尚未生成带参数的预编译语句,无法修正参数顺序。
解决方案
方案1:自定义Hibernate方言,修正分页语句生成逻辑
Firebird 2.5要求分页语法为first ? skip ?,但Hibernate 6.x默认的FirebirdDialect对原生查询的分页处理可能适配了更高版本的Firebird(允许skip在前)。我们可以自定义方言覆盖分页逻辑:
import org.hibernate.dialect.FirebirdDialect; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.FirebirdLimitHandler; public class CustomFirebird25Dialect extends FirebirdDialect { @Override public LimitHandler getLimitHandler() { return new FirebirdLimitHandler() { @Override public String processSql(String sql, boolean hasOffset) { if (hasOffset) { // 强制使用Firebird 2.5要求的first在前、skip在后的语法 return sql + " first ? skip ?"; } return super.processSql(sql, hasOffset); } }; } }
然后在Spring配置中指定使用自定义方言:
# application.properties spring.jpa.properties.hibernate.dialect=com.yourpackage.CustomFirebird25Dialect
方案2:手动在原生查询中编写分页逻辑
放弃依赖Pageable自动插入分页参数,手动计算偏移量并写入SQL,同时指定count查询来支持分页结果的总数统计:
修改仓库代码
@Query( value = "select t_m_brands.id...... where gmin <= :size and gmax >= :size first :pageSize skip :offset", countQuery = "select count(*) from t_m_brands where gmin <= :size and gmax >= :size", nativeQuery = true ) Page<MaterialBrand> findByMaterialSizesQuery( @Param("size") Float size, @Param("pageSize") int pageSize, @Param("offset") int offset );
控制器中计算参数
Pageable paging = PageRequest.of(1, 10); int offset = paging.getPageNumber() * paging.getPageSize(); Page<MaterialBrand> items = repository.findByMaterialSizesQuery(25, paging.getPageSize(), offset);
方案3:升级Firebird版本(可选)
Firebird 3.0及以上版本支持skip ... first ...的语法顺序,如果业务允许升级数据库,可以解决此兼容性问题。
内容的提问来源于stack exchange,提问作者Zadra
相关产品推荐
相关产品推荐

