如何将 String[] 参数设置为本机查询?

Posted

技术标签:

【中文标题】如何将 String[] 参数设置为本机查询?【英文标题】:How can I set a String[] parameter to a native query? 【发布时间】:2012-08-16 01:12:11 【问题描述】:

这是我的 PostgreSQL 函数:

salvarArquivoGeometricoCasoZeroPOINT
(dimensao text,tableName text,tuplas text[],srid text)

它有一个 text[] 参数,我想从我的 JPQL 向它传递一个 Java String[]

public String salvarGeometriaCaso0(String[] tuplas,FileDto arquivo)
        Query query =
        em().createNativeQuery("select 
salvarArquivoGeometricoCasoZeroPOINT(?1,?2,?3,?4)");
        query.setParameter(1,arquivo.getGeo());//String
        query.setParameter(2,arquivo.getTable());/String
        query.setParameter(3,tuplas);//String[]
        query.setParameter(4,arquivo.getSrid());//String
        return (String) query.getSingleResult();//function returns a Text, so cast to String

上述代码失败,异常:

ERROR] Internal Exception: org.postgresql.util.PSQLException: Can not infer a SQL
type to use for an instance of [Ljava.lang.String;. 
Use setObject () with an explicit Types value to specify the type to use.

所以我不确定如何从 EclipseLink 调用我的函数。

【问题讨论】:

您是否尝试过“[使用] setObject () 使用明确的Types 值来指定要使用的类型?” 你能告诉我怎么做吗? 您可以在PreparedStatement 上调用setObject() 而不是query.setParameter(3, tuplas)。 docs.oracle.com/javase/7/docs/api/java/sql/… 但我使用的是 EclipseLink。我必须遵循一些模式,我不能使用 PreparedStatement。 哪个 EclipseLink 版本?哪个 PgJDBC 版本?我以为 EclipseLink 和 PgJDBC 理解 String[] 映射到 text[] 【参考方案1】:

我很晚才回答。

这个解决方案是一种使用 postgreSQL 内置函数的解决方法,它绝对对我有用。

reference blog

1) 将字符串数组转换为逗号分隔的字符串

如果您使用的是 Java8,这很容易。其他选项是here

String commaSeparatedString = String.join(",",stringArray); // Java8 feature

2) PostgreSQL 内置函数 string_to_array()

你可以找到其他的postgreSQL数组函数here

// tableName ( name text, string_array_column_name text[] )

String query = "insert into tableName(name,string_array_column_name ) values(?, string_to_array(?,',') )";


int[] types = new int[]  Types.VARCHAR, Types.VARCHAR;

Object[] psParams = new Object[] "Dhruvil Thaker",commaSeparatedString ;

jdbcTemplate.batchUpdate(query, psParams ,types); // assuming you have jdbctemplate instance

【讨论】:

【参考方案2】:

通过将 String[] 类型的 Java 数组传递给 PreparedStatement.setObject(...) 进行测试会导致您报告的行为。 PgJDBC 似乎不接受 Java 数组作为 PreparedStatement.setObject() 的参数,无论是否带有 Types.ARRAY 参数。

合规

JDBC 规范 16.5 "Array Objects" 表明 JDBC Array 部分存在,因此客户端不必将大数组复制到内存中,它们可以通过引用来使用。我不太确定 JDBC 驱动程序是否需要接受原始 Java 数组作为参数。所有规范代码都引用了java.sql.Array,并且规范清楚地表明,数组是通过附录 B 和其他地方的Array 接口映射的。在快速搜索/阅读中,我发现没有提到将 byte[] 以外的原始 Java 数组作为参数传递或将它们作为结果返回。

但是,在 §16.5.4 中,JDBC4.2 草案规范如下:

A Java array may be passed as an input parameter by calling the method
PreparedSatement.setObject.

尽管所有其他代码都引用了Array 对象。他们是指“Java 数组”中的Array 吗?还是他们的意思是像 String[] 这样的原始原生 Java 数组?

在我看来,客户端应该通过Connection.createArrayOf(...) 使用java.sql.Array 接口,所以EclipseLink 可能做错了。

怎么办

尝试将 EclipseLink 更新到 2.4,希望它使用the commonly specified method of passing arrays to JDBC via a java.sql.Array object。

您可能还需要使用 EclipseLink 扩展 @Array 来注释映射。另见this forum thread re 2.3 和bug 361701。

看来您可能必须为 EclipseLink 实现自己的类型处理程序才能覆盖其行为。要通过 PgJDBC 正确设置数组参数,您必须使用:

    Array sqlArray = conn.createArrayOf("text", strArray);
    pstmt.setArray(1, sqlArray);
    pstmt.executeUpdate();

...其中connpstmt 分别是java.sql.ConnectionPreparedStatementstrArrayString[] 实例。

见Custom data types in the eclipselink wiki。

另一方面,考虑到java.sql.Types 的存在,使用字符串类型名称来指定createArrayOf 中的数组数据类型似乎有点疯狂。它使便携性变得更加困难;上面的代码不会在(比如说)Oracle 上运行,因为 Oracle 想要 VARCHAR 而不是 text 作为类型名称。


注意:单元测试org/postgresql/test/jdbc2/ArrayTest.javaArrayTest.testSetArray(),在第 166 行测试:

    pstmt.setObject(1, arr);
    pstmt.executeUpdate();

...但是arr 的类型是java.sql.Array,而不是int[]。它是 JDBC 数组类型,而不是常规的 Java 数组。

【讨论】:

更新 eclipseLink 不起作用。唯一的方法是使用纯 JDBC【参考方案3】:

EclipseLink 似乎无法修复@Craig Ringer 提到的bug 361701.。

将 String[] 作为参数传递的唯一方法是使用没有 EclipseLink 的 JDBC。检查代码。

Connection con = ConnectionHelper.getConnection();
        Array tArray = con.createArrayOf("text", tuplas);
        PreparedStatement pstm =
        con.prepareStatement("select salvarArquivoGeometricoCasoZeroPOINT(?,?,?,?)");
        pstm.setString(1,arquivo.getGeoType());
        pstm.setString(2,arquivo.getTable());
        pstm.setArray(3,tArray);
        pstm.setString(4,arquivo.getSrid());
        rs = pstm.executeQuery();

ConnectionHelper 这是我的 java.sql.Connection 类。

感谢你们的帮助:@Craig Ringer 和 @Matt Ball,谢谢。

【讨论】:

我想您也可以使用自定义数据类型工具为数组实现自己的 EclipseLink 数据类型处理程序。不过,下拉到普通的 JDBC 可能更容易。【参考方案4】:

我在不小心使用jdbcTemplate.update 而不是jdbcTemplate.batchUpdate 时遇到了这个错误

【讨论】:

以上是关于如何将 String[] 参数设置为本机查询?的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate:如何使用HQL设置NULL查询参数值?

本机查询中未设置参数

休眠。将字符串参数设置为 char 25

Hibernate HQL 查询:如何将集合设置为查询的命名参数?

如何将值列表设置为休眠查询的参数?

如何将函数参数键入为本机函数