迁移到Hibernate 6与Spring Boot 3后createNativeQuery无法正常工作的问题求助
迁移到Hibernate 6与Spring Boot 3后createNativeQuery无法正常工作的问题求助
这个问题我之前帮朋友排查过类似的,Hibernate 6相对于Hibernate 5在原生查询的参数类型推断上做了更严格的限制,尤其是枚举集合这类特殊类型,PostgreSQL没办法自动识别参数的类型,所以抛出了这个“无法确定参数$3的数据类型”的错误。下面给你几个可行的解决思路:
方法1:在SQL中显式转换参数为枚举类型
你的原SQL里还有个小疏漏:coalesce(statuses)应该写成coalesce(:statuses)(少了冒号),不然PostgreSQL会把statuses当成列名处理。修改后的SQL通过unnest展开集合参数,再显式转换为你的枚举类型as_status_type,让PostgreSQL明确知道参数类型:
select * from earp.as sys where (sys.id = :id or sys.parent_id = :id) and (coalesce(:statuses) is null or (sys.status not in (select cast(unnest(:statuses) as as_status_type))) )
方法2:在设置参数时显式指定枚举类型
Hibernate 6允许我们在设置参数时直接指定类型,告诉Hibernate这是一个枚举集合,这样PostgreSQL就能正确识别类型了。你可以用EnumType来指定:
return entityManager.unwrap(Session.class) .createNativeQuery(sql, Tuple.class) .setParameter("id", id) // 如果你是用字符串枚举映射 .setParameter("statuses", statuses, EnumType.STRING.asParameterType()) // 如果你是直接映射数据库枚举类型,用下面的方式(需要引入Hibernate的EnumType) // .setParameter("statuses", statuses, new EnumType<>(AsStatusType.class, EnumType.NamingStrategy.EXPLICIT)) .getResultStream();
方法3:提前处理空集合的分支逻辑
Hibernate 6对空集合参数的处理逻辑和Hibernate 5不同,你可以在Java代码里提前判断statuses是否为空,动态拼接SQL,避免空集合导致的类型推断问题:
// 基础SQL StringBuilder sqlBuilder = new StringBuilder("select * from earp.as sys where (sys.id = :id or sys.parent_id = :id)"); // 先初始化基础查询 var baseQuery = entityManager.unwrap(Session.class) .createNativeQuery(sqlBuilder.toString(), Tuple.class) .setParameter("id", id); // 只有当statuses非空时,才添加条件并设置参数 if (statuses != null && !statuses.isEmpty()) { sqlBuilder.append(" and sys.status not in (:statuses)"); // 重新生成包含新条件的查询 return entityManager.unwrap(Session.class) .createNativeQuery(sqlBuilder.toString(), Tuple.class) .setParameter("id", id) .setParameter("statuses", statuses, EnumType.STRING.asParameterType()) .getResultStream(); } return baseQuery.getResultStream();
另外,还要检查一下你的枚举类型映射是否正确,Hibernate 6中枚举的JPA注解可能需要调整,比如用@JdbcTypeCode(SqlTypes.ENUM)来明确指定是数据库枚举类型,不过这里是原生查询,主要还是参数类型的问题。
备注:内容来源于stack exchange,提问作者Mike Sonarov
相关产品推荐
相关产品推荐

