从javax迁移到jakarta后PostgreSQL报lower(bytea)不存在错误排查
根本原因
Hibernate 6.x(配合Spring Boot 3.x)的SQM查询解析逻辑相比5.x发生了变化:即使你写了(:createdBy is null OR ...)的短路条件,Hibernate在生成SQL时并不会直接跳过右侧表达式的类型解析。当:createdBy参数为null时,Hibernate无法正确推断其原本的String类型,反而将其映射为bytea类型,导致PostgreSQL执行lower(bytea)时找不到对应函数报错。
而你在PostgreSQL命令行手动执行正常,是因为手动输入的null会被PostgreSQL根据上下文(和varchar类型的o.createdBy比较)推断为正确的varchar类型,不会触发bytea的问题。
解决方案
1. 显式指定参数类型转换
在JPQL中对:createdBy参数显式做类型转换,确保Hibernate识别其为String类型:
SELECT o FROM Order o WHERE (o.createdAt between :from and :to) AND (:query is null OR function('pgTextSearch', :query, o) = true) AND (:createdBy is null OR LOWER(o.createdBy) = LOWER(CAST(:createdBy AS string)))
通过CAST(:createdBy AS string)强制指定参数类型,避免Hibernate将null推断为bytea。
2. 重构条件逻辑,避免null参数进入lower函数
调整条件写法,确保只有当:createdBy非null时才执行lower比较,同时处理o.createdBy为null的场景(如果业务允许):
SELECT o FROM Order o WHERE (o.createdAt between :from and :to) AND (:query is null OR function('pgTextSearch', :query, o) = true) AND (:createdBy is null OR (o.createdBy is not null AND LOWER(o.createdBy) = LOWER(:createdBy)))
这种写法能让Hibernate更清晰地识别短路逻辑,减少类型推断出错的概率。
3. 使用Spring Data JPA的动态查询(推荐)
放弃在JPQL中写短路条件,改用Specification构建动态查询,当:createdBy为null时直接不添加该条件:
public List<Order> findOrders(Date from, Date to, String query, String createdBy) { return orderRepository.findAll((root, querySpec, criteriaBuilder) -> { Predicate predicate = criteriaBuilder.between(root.get("createdAt"), from, to); if (query != null) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(criteriaBuilder.function("pgTextSearch", Boolean.class, criteriaBuilder.literal(query), root), true)); } if (createdBy != null) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(criteriaBuilder.lower(root.get("createdBy")), criteriaBuilder.lower(criteriaBuilder.literal(createdBy)))); } return predicate; }); }
这种方式从根源上避免了null参数进入JPQL表达式,完全由代码控制查询条件的添加,更可靠。
4. 调整Hibernate类型映射配置
确认实体类中createdBy字段的映射正确后,可在配置文件中添加Hibernate类型绑定规则,强制String类型的null参数映射为varchar:
hibernate.type.preferred_string_jdbc_type=VARCHAR
内容的提问来源于stack exchange,提问作者RainerZufall

