postgresql - 如何将 Postgres any 子句与 JPA/Hibernate native 查询(数组参数)一起使用

标签 postgresql jpa parameter-passing nativequery

所以我们有大量的 Postgres SQL 查询存储在文件中并从 PHP 中使用。任务是用 Java 替换 PHP。我们希望“按原样”重用尽可能多的查询,以缩短迁移路径。我无法使 Array 参数起作用。

这是一个查询示例:

update user_devices
set some_date = now()
where some_id in (
    select distinct some_id from user_devices 
    where user_id = any(:userIDs) and device_id = any(:deviceIDs)
    and exists (select 1 from users where user_id = any(:userIDs) and customer_id = :customerID)
);

请注意导致问题的“any”子句,因为它们需要数组类型。 这就是我们在 PHP 中使用它们的方式:

$this->allValues['userIDs'] = '{' . implode ( ",", $userIdNodes ) . '}';
$this->allValues['deviceIDs'] = '{' . implode ( ",", $deviceIdNodes ) . '}';
$this->allValues['customerID'] = customerID;
$this->db->runQuery ( $this->getQuery ( 'my_query' ), $this->allValues );

因此作为参数,数组类型看起来像“{111,222}”。

这是我在 Java 中尝试过的:

    Integer customerID = 1;
    int[] userIDs  = new int[]{111,222};
    int[] deviceIDs= new int[]{333,444};
    //List<Integer> userIDs  = Arrays.asList(111,222);
    //List<Integer> deviceIDs= Arrays.asList(333,444);
    //java.sql.Array userIDs  = toArray("integer", new int[]{111,222}));
    //java.sql.Array deviceIDs= toArray("integer", new int[]{333,444}));
    //java.sql.Array userIDs  = toArray("integer", Arrays.asList(111,222)));
    //java.sql.Array deviceIDs= toArray("integer", Arrays.asList(333,444)));
    //String userIDs  = "{111,222}";
    //String deviceIDs= "{333,444}";
    //String userIDs  = "ARRAY[111,222]";
    //String deviceIDs= "ARRAY[333,444]";

    Query nativeQuery = em.createNativeQuery(queryString);
    nativeQuery.setParameter("userIDs", userIDs);
    nativeQuery.setParameter("deviceIDs", deviceIDs);
    nativeQuery.setParameter("customerID", customerID);
    //nativeQuery.setParameter(createParameter("userIDs",java.sql.Array.class), userIDs);
    //nativeQuery.setParameter(createParameter("userIDs",java.sql.Array.class), deviceIDs);
    //nativeQuery.setParameter(createParameter("customerID", Integer.class), customerID);
    query.executeUpdate();

//[...]
private Array toArray(String typeName, Object... elements) {
    Session session = em.unwrap(Session.class); // ATTENTION! This is Hibernate-specific!
    final AtomicReference<Array> aRef = new AtomicReference<>();
    session.doWork((c) -> {
        aRef.set(c.createArrayOf(typeName, elements));
    });
    return aRef.get();
}

private <T> Parameter<T> createParameter(final String name, final Class<?> clazz) {
    return new Parameter<T>() {
        @Override
        public String getName() {
            return name;
        }
        @Override
        public Integer getPosition() {
            return null; // not used
        }
        @Override
        public Class<T> getParameterType() {
            return (Class<T>) clazz;
        }
    };
}

这些都不起作用我将得到以下异常之一: 使用“toArray”方法时:

Caused by: org.hibernate.HibernateException: Could not determine a type for class: org.postgresql.jdbc4.Jdbc4Array
    at org.hibernate.internal.AbstractQueryImpl.guessType(AbstractQueryImpl.java:550)
    at org.hibernate.internal.AbstractQueryImpl.guessType(AbstractQueryImpl.java:534)
    at org.hibernate.internal.AbstractQueryImpl.determineType(AbstractQueryImpl.java:519)
    at org.hibernate.internal.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:487)
    at org.hibernate.jpa.internal.QueryImpl$ParameterRegistrationImpl.bindValue(QueryImpl.java:247)
    at org.hibernate.

或者当使用 int[] 或字符串时,我会得到:

Caused by: org.postgresql.util.PSQLException: ERROR: op ANY/ALL (array) requires array on right side
  Position: 137
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2270)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1998)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:570)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:420)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:366)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.postgresql.ds.jdbc23.AbstractJdbc23PooledConnection$StatementHandler.invoke(AbstractJdbc23PooledConnection.java:453)
    at com.sun.proxy.$Proxy274.executeUpdate(Unknown Source)
    at com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:125)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
    jpa.spi.BaseQueryImpl.setParameter(BaseQueryImpl.java:582)

当两个 API 都与数据库对话时,我使用 Wireshark 发现了这一点:

Image: Comparison of database calls with Wireshark

select oid, typname from pg_type where oid in (0, 23, 1043) order by oid;

oid   |typname
------+-------
23    |int4
1043  |varchar

有没有人设法将数组参数与使用 Hibernate 作为 JPA EntityManager 后端的 native 查询一起使用?如果是这样:如何?

最佳答案

where user_id = any(:userIDs) 更改您的查询至 where user_id IN (:userIDs) , 并更改 userIDs数组到集合,例如List<Long> .您将不得不额外保护它的空列表,但它会起作用。

关于postgresql - 如何将 Postgres any 子句与 JPA/Hibernate native 查询(数组参数)一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36601318/

相关文章:

angularjs - 在某个时间点监控事件的数据库连接

postgresql - 关于使用 Ecto 预填充 PostgreSQL 表的建议

Spring Data JPA 自定义通用存储库模式问题

java - 自动设置反向引用

ios - 使用InstantiateViewControllerWithIdentifier通过UITableViewController传递数据

java - 如何将数组和当前索引传递给 Java 中的方法

sql-server - 即时将 SQL Server 查询转换为 Postgres

sql - 从子查询搜索结果中选择记录 (postgres)

java - jpa 来自 MappedSuperclass 的可嵌入继承

java - 将不同的类作为参数传递给 Java 函数