2016-04-13 31 views
0

Postgres SQLクエリはファイルに格納され、PHPから使用されています。この作業は、PHPをJavaに置き換えることです。移行パスを短くするために、「そのまま」のクエリを再利用したいと考えています。私は配列パラメータを動作させることができません。ここでJPA/Hibernateネイティブクエリ(配列パラメータ)でPostgres any-clauseを使用する方法

は、クエリの例です:彼らは配列型を期待しているため

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) 
); 

は、問題を引き起こす「任意の」の句を、注意してください。 これは、我々はPHPからそれらを使用する方法です。だから、「{111222}」の配列型は次のようになり、パラメータとして

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

これは私がJavaで試したものです:

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. 

または: "のtoArray" メソッドを使用している場合 :これらの

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; 
     } 
    }; 
} 

どれも、私はこれらの例外のいずれかを取得します動作しませんint []やStringsを使用する場合は、

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 

を誰もがJPAのEntityManagerのためのバックエンドとして休止状態を使用して、ネイティブクエリで配列パラメータを使用するように管理していますか?もしそうなら:どのように?

答えて

1

この問題を回避するには、EntityManagerからHibernateセッションをアンラッピングして、JDBC PreparedStatementを使用します。このPreparedStatementは、java.sql.Arrayパラメータを不平を取らずに取ります。

以下の例で使用されているNamedParameterStatementは、here(私は自分のニーズに合わせて変更しました)と記載されています。これはPreparedStatementに委譲します。

コードの残りの部分は、このような少し何か行く:

public int executeUpdate(...){ 
    //.... 
    Integer customerID = 1; 
    java.sql.Array userIDs = toArray("integer", new int[]{111,222})); 
    java.sql.Array deviceIDs= toArray("integer", new int[]{333,444})); 

    final AtomicInteger rowsModifiedRef = new AtomicInteger(); 
    final Session session = em.unwrap(Session.class); // ATTENTION! This is Hibernate-specific! 
    session.doWork((c) -> { 
     try (final NamedParameterStatement statement = new NamedParameterStatement(c, queryString)) { 
      statement.setObject("deviceIDs", userIDs); 
      statement.setObject("userIDs", userIDs); 
      statement.setObject("customerID", userIDs); 
      rowsModifiedRef.set(statement.executeUpdate()); 
     } 
    }); 
    return rowsModifiedRef.get(); 
} 

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(); 
} 
関連する問題