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

10

这是我的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值来指定要使用的类型”吗? - Matt Ball
你能向我展示如何做这个吗? - Bernardo Vale
不是调用 query.setParameter(3, tuplas),而是在 PreparedStatement 上调用 setObject()。http://docs.oracle.com/javase/7/docs/api/java/sql/PreparedStatement.html - Matt Ball
但是我正在使用EclipseLink。我必须遵循一些规则,我不能使用PreparedStatement。 - Bernardo Vale
哪个EclipseLink版本?以及哪个PgJDBC版本?我认为EclipseLink和PgJDBC都理解String[]映射到text[] - Craig Ringer
@Craig Ringer:是的,我有一个返回text[]的函数,并且我通过将对象转换为String[]来捕获结果。String[] result = (String[])query.getSingleResult; 但是将String[]设置为参数会返回该错误。我正在使用EclipseLink 3.2和Postgres 9.1。 - Bernardo Vale
4个回答

7

我很晚才回答。

这个解决方案是通过使用postgreSQL内置函数的一种解决方法,对我来说肯定有效。

参考博客

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

如果您正在使用Java8,那么非常容易。其他选项在这里

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

2) PostgreSQL内置函数string_to_array()

您可以在此处找到其他的PostgreSQL数组函数。

// 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

6
将类型为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以期望它使用通过java.sql.Array对象通常指定的向JDBC传递数组的方法。 您还需要使用EclipseLink扩展注释映射@Array。请参见此2.3论坛线程bug 361701
看起来您可能需要实现自己的类型处理程序以覆盖EclipseLink的行为。要通过PgJDBC正确设置数组参数,必须使用:
    Array sqlArray = conn.createArrayOf("text", strArray);
    pstmt.setArray(1, sqlArray);
    pstmt.executeUpdate();

在这里,connpstmt 都是 java.sql.Connection 类型的实例,而 strArray 是一个 String[] 实例。

请参考eclipselink wiki 上的自定义数据类型

顺便提一下,在使用 createArrayOf 方法中用字符串类型名称来指定数组的数据类型似乎有点荒谬,因为存在 java.sql.Types。 这会增加可移植性的难度。 上面的代码在(比如)Oracle 上无法运行,因为 Oracle 要求类型名为 VARCHAR 而不是 text


注意:org/postgresql/test/jdbc2/ArrayTest.java 单元测试中的 ArrayTest.testSetArray() 在第 166 行测试了:

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

然而,arr 的类型是 java.sql.Array,而不是 int[]。它是 JDBC 数组类型,不是普通的 Java 数组。


更新EclipseLink没有起作用,唯一的方法是使用纯JDBC。 - Bernardo Vale

1

看起来EclipseLink没有修复@Craig Ringer提到的bug 361701.

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

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可能更容易些。 - Craig Ringer

0

我在编程时不小心使用了 jdbcTemplate.update 而不是 jdbcTemplate.batchUpdate,导致出现了这个错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接