将 List<String> 作为参数传递给 postgres 函数
Posted
技术标签:
【中文标题】将 List<String> 作为参数传递给 postgres 函数【英文标题】:Pass List<String> into postgres' function as parameter 【发布时间】:2019-08-05 18:39:33 【问题描述】:我有这样的spring数据存储界面:
public interface MyEntityRepository extends
JpaRepository<MyEntity, Long>
@Query(nativeQuery = true, value = "select * from my_func(:myList)")
Page<MyEntity> findBy(
@NonNull @Param("myList") List<String> myList,
@NonNull Pageable pageable);
Postgres 的函数我是这样定义的(但如果我做错了我可以改变它):
CREATE OR REPLACE FUNCTION my_func(variadic myList text[])
RETURNS SETOF myEntityTable AS $$
... some logic
select * from myEntityTable t where t.foo in (myList);
当我调用这个存储库方法时,我得到了这个错误:
ERROR: operator does not exist: character varying = text[]
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Where: PL/pgSQL function f_najdi_autorizaciu_na_spracovanie(text[]) line 28 at RETURN QUERY
你能告诉我我应该在我的 postgres 函数中使用什么类型吗?谢谢你的建议。
编辑: 我不能在存储库方法上使用本机查询并将那里的列表传递给 IN 子句,因为我在 DB 函数中有更多的逻辑、变量等......它必须是 DB 函数。
【问题讨论】:
Postgres 中的数组不与任何其他语言的任何其他集合相同。他们... 不同。你能解释一下你想用my_func
完成什么吗?传统 SQL 中可能有更优雅的解决方案。
@Makoto 嗨,我编辑了我的问题。在我的函数中,我有一些带有光标的高级逻辑,等等。这个传递的参数我只需要在 IN 子句中 where 条件的最终选择中使用
所以我相信这是一个 XY 问题 - 您正在尝试根据 IN 查询查找一组记录,并且有一种比您建议的更简单的方法来执行此操作。我为这个问题找到了一个骗子;我鼓励你仔细阅读它。如果不合适,您可以编辑您的问题以解释原因。
Spring CrudRepository findByInventoryIds(List<Long> inventoryIdList) - equivalent to IN clause的可能重复
看起来列表已成功传递(您在遇到错误之前已将其移至函数的第 28 行)。我猜原因是WHERE t.foo IN (myList)
;要检查 t.foo
是否在您的数组中,请改用 t.foo = ANY(myList)
。
【参考方案1】:
不确定这是否会增加很多价值,我只是希望它有所帮助。
PostgreSQL 函数期望从列表作为输入的最终格式是以下模式,使用数组:
select my_func(array['item1','item2']::my_type[]);
查看完整示例 on Database Administrators.SE。
在你的情况下:
select my_func(array['item1','item2']::text[]);
我在自己的测试中发现
select my_func(array['item1','item2']);
应该已经足够了。
然后,唯一的目的是从您的原始 java 类型中获取此格式。另一个问题已经回答了这个问题。这个答案只是为了表明这一切的目的。
【讨论】:
【参考方案2】:我在类似情况下使用了以下变通解决方案:
1) 创建了两个辅助函数:
-- Convert a variable number of text arguments to text array
-- Used to convert Java collection to the text array
--
create or replace function list_to_array(variadic _list text[]) returns text[] language sql as $$
select _list;
$$;
-- Convert the bytea argument to null.
-- Used to convert Java null to PostgreSQL null
--
create or replace function list_to_array(_list bytea) returns text[] language sql as $$
select null::text[];
$$;
2) 在主函数中使用any
而不是in
,例如:
create or replace function my_func(_params text[])
returns table (field1 text, field2 text)
language sql as
$$
select
t.field1 as field1,
t.field2 as field2,
from
my_table t
where
array_length(_params, 1) is null or t.foo = any(_params);
$$;
3) 然后在存储库方法中使用它们,例如:
@NonNull
@Query(value = "select ... from my_func(list_to_array(?1))", nativeQuery = true)
List<MyProjection> getFromMyFunc(@Nullable Set<String> params, @NonNull Pageable page);
【讨论】:
【参考方案3】:恐怕我不知道如何使用 Spring Data JPA 执行此操作,但使用普通 JDBC,您只需将绑定变量强制转换为 text[]
并传递 String[]
类型而不是列表。例如:
try (PreparedStatement s = conn.prepareStatement("select * from my_func(?::text[])"))
s.setObject(1, myList.toArray(new String[0]));
try (ResultSet rs = s.executeQuery())
// ...
这里的关键信息是 JDBC 驱动程序需要一个数组,而不是一个列表。
【讨论】:
感谢您的回答,但 Jdbc 模板是我们最后的选择。我们还考虑在 spring 的数据 jpa 有很好的 api 的地方进行分页。 @DenisStephanov 鉴于您已经在该功能中做了很多工作,为什么不将分页也移入其中呢?让函数返回整个结果集只是为了事后分页似乎很浪费...... 因为我们有复杂的前端设计。我们收到带有排序属性等的可分页对象。 我也尝试了您的解决方案,但我得到错误函数 my_func(text[]) 不存在。我检查了分贝,我确定有。我也将其参数编辑为 text[] 没有可变参数但没有帮助 @DenisStephanov:您可能需要使用架构限定函数以上是关于将 List<String> 作为参数传递给 postgres 函数的主要内容,如果未能解决你的问题,请参考以下文章
当我尝试使用 List<KeyValuePair<string, string>> 作为参数之一调用成员时,Type.InvokeMember 引发 MissingMethod
使用 JDBCTemplate 在“IN”子句中将 List<String> 作为查询参数传递时获取 PSQL 异常
c#中 如何用List<string>作为Dictionary的key。