JPA原生查询中如何向PostgreSQL传递数组类型参数
你写的array[:userRoles]从根上就错了:JPA不会把传入的Java String[]展开成PG数组构造器的多个元素,反而会把整个数组对象当成数组里的单个值,最后生成的是类似array[ARRAY['Role1','Role2']]的嵌套数组,和jsonb_exists_any要求的一维text[]参数类型完全不匹配。你之前试的array[CAST(:userRoles to jsonb)]这类写法,本质还是在错误的嵌套结构里转类型,当然不可能生效。
方案1:显式类型转换(无额外依赖,适配绝大多数Spring Data JPA + Hibernate环境)
直接删掉你自己加的array[]包装,把传入的Java集合/数组显式转成PostgreSQL的text[]类型即可,代码如下:@Query(value = "SELECT * FROM dashboards d WHERE jsonb_exists_any(d.roles_access, CAST(:userRoles AS text[]))", nativeQuery = true) List<Dashboard> findAllDashboardsByUserRole(@Param("userRoles") final String[] userRoles);如果你用的JPA版本对CAST关键字解析有兼容问题,可以换PostgreSQL原生的双冒号类型转换语法,注意参数前缀的冒号需要转义:
@Query(value = "SELECT * FROM dashboards d WHERE jsonb_exists_any(d.roles_access, :userRoles\\:\\:text[])", nativeQuery = true) List<Dashboard> findAllDashboardsByUserRole(@Param("userRoles") final String[] userRoles);如果你习惯用
List<String>传参而非数组,上述SQL完全兼容,只需要把方法参数类型改成List<String>即可,不需要调整其他逻辑。方案2:用等价操作符简化写法
jsonb_exists_any本质是PostgreSQL内置?|操作符的函数封装,写法更简洁,参数绑定逻辑和方案1完全一致:@Query(value = "SELECT * FROM dashboards d WHERE d.roles_access ?| CAST(:userRoles AS text[])", nativeQuery = true) List<Dashboard> findAllDashboardsByUserRole(@Param("userRoles") final String[] userRoles);方案3:低版本Hibernate适配
如果你用的Hibernate版本低于5.4,内置数组类型映射有缺陷,无法自动识别Java数组和PG数组的映射关系,可以借助hibernate-types库显式指定参数类型,写法如下:import com.vladmihalcea.hibernate.type.array.StringArrayType; import org.hibernate.annotations.Type; @Query(value = "SELECT * FROM dashboards d WHERE jsonb_exists_any(d.roles_access, :userRoles)", nativeQuery = true) List<Dashboard> findAllDashboardsByUserRole( @Param("userRoles") @Type(StringArrayType.class) final String[] userRoles );
注意:不要图省事把角色列表拼成逗号分隔的字符串,再用
string_to_array函数转数组,这种写法一旦角色名本身包含逗号就会出现匹配错误,生产环境不推荐使用。
内容的提问来源于stack exchange,提问作者harsha vanama

