将 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<string> 传递给 SQL 参数

当我尝试使用 List<KeyValuePair<string, string>> 作为参数之一调用成员时,Type.InvokeMember 引发 MissingMethod

使用 JDBCTemplate 在“IN”子句中将 List<String> 作为查询参数传递时获取 PSQL 异常

c#中 如何用List<string>作为Dictionary的key。

如何将 List<string> 作为对象插入 SQL Server 表中?

泛型理解