【问题标题】:Pass List<String> into postgres' function as parameter将 List<String> 作为参数传递给 postgres 函数
【发布时间】: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 查询查找一组记录,并且有一种比您建议的更简单的方法来执行此操作。我为这个问题找到了一个骗子;我鼓励你仔细阅读它。如果不合适,您可以编辑您的问题以解释原因。
  • 看起来列表已成功传递(您在遇到错误之前已将其移至函数的第 28 行)。我猜原因是WHERE t.foo IN (myList);要检查 t.foo 是否在您的数组中,请改用 t.foo = ANY(myList)

标签: java postgresql spring-data-jpa spring-data plpgsql


【解决方案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:您可能需要使用架构限定函数
      猜你喜欢
      • 2015-11-20
      • 2014-04-06
      • 2013-12-04
      • 2013-01-27
      • 2020-08-31
      • 2016-06-28
      • 2021-04-13
      • 1970-01-01
      • 2012-12-02
      相关资源
      最近更新 更多