1

アップデート:

spring-data-jpa リポジトリ クエリ メソッドのソース コードを掘り下げたところ、根本的な原因は以下のPartTreeJpaQuerycreateQueryのメソッドであることがわかりました。

このメソッドは、spring-data-jpa リポジトリ クエリ メソッド インターフェースを呼び出すときに、RepositoryFactorySupportのQueryExecutorMethodInterceptorのinvokeメソッドによって呼び出されます。

public Query createQuery(Object[] values) {

    CriteriaQuery<?> criteriaQuery = cachedCriteriaQuery;
    List<ParameterMetadata<?>> expressions = this.expressions;
    ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values);

    if (cachedCriteriaQuery == null || accessor.hasBindableNullValue()) {
        JpaQueryCreator creator = createCreator(accessor, persistenceProvider);
        criteriaQuery = creator.createQuery(getDynamicSort(values));
        expressions = creator.getParameterExpressions();
    }

    TypedQuery<?> jpaQuery = createQuery(criteriaQuery);

    return restrictMaxResultsIfNecessary(invokeBinding(getBinder(values, expressions), jpaQuery));
}

最初の呼び出しでは、cachedCriteriaQuery変数は期待どおりでSELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.idあり、values変数は[[1000000002], [5, 10]]どちらも正しいです。

ただし、2 回目の呼び出しでは、cachedCriteriaQueryisSELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.idvaluesis [[1000000003], [5]]. openjpa および jpa クエリに渡される値は正しいように見えますが、criteriaQueryそうではありません。

openjpaライブラリをバージョン2.2.2に変更した場合、このメソッドを呼び出すたびに、cachedCriteriaQuery変数はALWAYS SELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.idになります。

  • cachedCriteriaQuery変数が変更される理由

    これは、PartTreeJpaQuery の QueryPreparer のプライベートかつ最終的なフィールドであり、それを割り当てることができる唯一の方法は、QueryPreparer のコンストラクターでなければなりません。そこでブレークポイントを切り替えましたが、1 回目と 2 回目の呼び出しの間に、このコンストラクターを呼び出したプロセスが見られませんでした。この変数はどのように変更されるのでしょうか?

  • 私が行ったのは、openjpa ライブラリを変更しただけです。

    ただし、上記のコードは spring-data-jpa および/または spring-data-commons です。これは、クエリ作成の動作にどのような影響を与える可能性がありますか?


フィールドとフィールドでサブタスク エンティティをクエリfind INするには、単体テスト コードを次のように指定します。最初のクエリは find IN deviceId and、state andです。2 回目のクエリは find IN deviceIdと stateです。deviceIdstate100000000251010000000035

List<Subtask> subtasks = this.subtaskDao.findByDeviceIdInAndStateInOrderByIdAsc(Arrays.asList(new String[]{"1000000002"}), Arrays.asList(new Integer[]{5, 10}));
System.out.println("1" + subtasks);
print(subtasks);
Thread.sleep(8000);
List<Subtask> subtasks1 = this.subtaskDao.findByDeviceIdInAndStateInOrderByIdAsc(Arrays.asList(new String[]{"1000000003"}), Arrays.asList(new Integer[]{5}));
System.out.println("2" + subtasks1);
print(subtasks1);

private void print(List<Subtask> subtasks)
{
    for (Subtask subtask : subtasks)
    {
        System.out.println(subtask.getId() + ", " + subtask.getDeviceId());
    }
}

subtaskDao は以下のようなspring-data-jpa repository query methodインターフェースです

public interface SubtaskDao extends DaoBase<Subtask, Long>
{
    public List<Subtask> findByDeviceIdInAndStateInOrderByIdAsc(@Param("deviceIdList") Collection<String> deviceIdList, @Param("states")  Collection<Integer> states);
}

ただし、結果は以下のとおりであり、期待どおりには正しくありません。2 回目のクエリの結果は、 deviceId1000000002ではなくdeviceId のサブタスクになり1000000003ます。

openjpa.Log によると、初回はクエリ(7272行目) Query "SELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.id"。しかし、2回目はクエリを実行しますQuery "SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id"パラメータがとでないのはなぜですか?:deviceIdList:states

7248  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Runtime - Found datasource1: datasource 193492784 from configuration. StoreContext: org.apache.openjpa.kernel.BrokerImpl@3344d163
7248  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Runtime - org.apache.openjpa.persistence.EntityManagerFactoryImpl@21362712 created EntityManager org.apache.openjpa.persistence.EntityManagerImpl@3344d163.
7272  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Runtime - Query "SELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.id" is cached."    
7273  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Query - Executing query: [Query: org.apache.openjpa.kernel.QueryImpl@5403907; candidate class: class devicemanage.repository.appdeploy.entity.Subtask; query: null] with parameters: ?
7278  PersistenceUnitAppDeploy  TRACE  [main] openjpa.DataCache - Cache miss while looking up key "org.apache.openjpa.datacache.QueryKey@35db94c8[query:[SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id],access path:[devicemanage.repository.appdeploy.entity.Subtask],subs:true,ignoreChanges:false,startRange:0,endRange:9223372036854775807,timeout:-1]".
7317  PersistenceUnitAppDeploy  TRACE  [main] openjpa.jdbc.SQL - <t 2066366456, conn 178371348> executing prepstmnt 1924108520 
SELECT t0.id, t0.despair_count, t0.device_id, t0.domain_id, t0.finish_time, t0.remark, t0.state, t0.task_id, t0.task_type, t0.version 
    FROM subtask t0 
    WHERE (t0.device_id = ? AND t0.device_id IS NOT NULL AND (t0.state = ? OR t0.state = ?) AND t0.state IS NOT NULL) 
    ORDER BY t0.id ASC 
[params=?, ?, ?]
7324  PersistenceUnitAppDeploy  TRACE  [main] openjpa.jdbc.SQL - <t 2066366456, conn 178371348> [7 ms] spent
7357  PersistenceUnitAppDeploy  TRACE  [main] openjpa.DataCache - Put key "org.apache.openjpa.datacache.QueryKey@35db94c8[query:[SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id],access path:[devicemanage.repository.appdeploy.entity.Subtask],subs:true,ignoreChanges:false,startRange:0,endRange:9223372036854775807,timeout:-1]" into cache.
7358  PersistenceUnitAppDeploy  TRACE  [main] openjpa.jdbc.JDBC - <t 2066366456, conn 0> [0 ms] close
7360  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Runtime - Query "SELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.id" is removed from cache  excluded permanently. Query "SELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.id" is not cached because its result is not obtained by executing a select statement. This can happen if the query was evaluated in-memory. The result was provided by org.apache.openjpa.datacache.QueryCacheStoreQuery$CachingResultObjectProvider.  .
7361  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Runtime - org.apache.openjpa.persistence.EntityManagerImpl@3344d163.close() invoked.
1[devicemanage.repository.appdeploy.entity.Subtask@16f15ae9, devicemanage.repository.appdeploy.entity.Subtask@6206b4a7, devicemanage.repository.appdeploy.entity.Subtask@77896335]
981, 1000000002
982, 1000000002
983, 1000000002
15371  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Runtime - Found datasource1: datasource 193492784 from configuration. StoreContext: org.apache.openjpa.kernel.BrokerImpl@5d14e99e
15371  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Runtime - org.apache.openjpa.persistence.EntityManagerFactoryImpl@21362712 created EntityManager org.apache.openjpa.persistence.EntityManagerImpl@5d14e99e.
15372  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Runtime - Query "SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id" is cached."   
15372  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Query - Executing query: [Query: org.apache.openjpa.kernel.QueryImpl@38cfecf3; candidate class: class devicemanage.repository.appdeploy.entity.Subtask; query: null] with parameters: ?
15372  PersistenceUnitAppDeploy  TRACE  [main] openjpa.DataCache - Cache miss while looking up key "org.apache.openjpa.datacache.QueryKey@35db9909[query:[SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id],access path:[devicemanage.repository.appdeploy.entity.Subtask],subs:true,ignoreChanges:false,startRange:0,endRange:9223372036854775807,timeout:-1]".
15373  PersistenceUnitAppDeploy  TRACE  [main] openjpa.jdbc.SQL - <t 2066366456, conn 178371348> executing prepstmnt 632104437 
SELECT t0.id, t0.despair_count, t0.device_id, t0.domain_id, t0.finish_time, t0.remark, t0.state, t0.task_id, t0.task_type, t0.version 
    FROM subtask t0 
    WHERE (t0.device_id = ? AND t0.device_id IS NOT NULL AND (t0.state = ? OR t0.state = ?) AND t0.state IS NOT NULL) 
    ORDER BY t0.id ASC 
[params=?, ?, ?]
15374  PersistenceUnitAppDeploy  TRACE  [main] openjpa.jdbc.SQL - <t 2066366456, conn 178371348> [1 ms] spent
15375  PersistenceUnitAppDeploy  TRACE  [main] openjpa.DataCache - Put key "org.apache.openjpa.datacache.QueryKey@35db9909[query:[SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id],access path:[devicemanage.repository.appdeploy.entity.Subtask],subs:true,ignoreChanges:false,startRange:0,endRange:9223372036854775807,timeout:-1]" into cache.
15375  PersistenceUnitAppDeploy  TRACE  [main] openjpa.jdbc.JDBC - <t 2066366456, conn 0> [0 ms] close
15375  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Runtime - Query "SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id" is removed from cache  excluded permanently. Query "SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id" is not cached because its result is not obtained by executing a select statement. This can happen if the query was evaluated in-memory. The result was provided by org.apache.openjpa.datacache.QueryCacheStoreQuery$CachingResultObjectProvider.  .
15375  PersistenceUnitAppDeploy  TRACE  [main] openjpa.Runtime - org.apache.openjpa.persistence.EntityManagerImpl@5d14e99e.close() invoked.
2[devicemanage.repository.appdeploy.entity.Subtask@1860a7a1, devicemanage.repository.appdeploy.entity.Subtask@40d96578, devicemanage.repository.appdeploy.entity.Subtask@c97721b]
981, 1000000002
982, 1000000002
983, 1000000002

spring使用した、spring-data-jpajdbc driverおよびライブラリのバージョンはopenjpa次のとおりで、jdk は1.8、データベースはPostgreSQL 9.4 windows versionです。

<springVersion>4.3.0.RELEASE</springVersion>
<springDataJpaVersion>1.10.2.RELEASE</springDataJpaVersion>
<openjpaVersion>2.4.1</openjpaVersion>
<postgreSQLJDBCVersion>9.4.1208</postgreSQLJDBCVersion>

エンティティ クラスは、ビルド時に次のように拡張されます。openjpa-maven-plugin

<plugin>
    <groupId>org.apache.openjpa</groupId>
    <artifactId>openjpa-maven-plugin</artifactId>
    <version>${openjpaVersion}</version>
    <executions>
        <execution>
            <id>enhancer</id>
            <phase>process-classes</phase>
            <goals>
                <goal>enhance</goal>
            </goals>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>org.apache.openjpa</groupId>
            <artifactId>openjpa</artifactId>
            <version>${openjpaVersion}</version>
        </dependency>
    </dependencies>
    <configuration>
        <includes>devicemanage/repository/**/*.class</includes>
        <addDefaultConstructor>true</addDefaultConstructor>
        <enforcePropertyRestrictions>true</enforcePropertyRestrictions>
    </configuration>
</plugin> 

EntityManagerFactoryBean設定 とは次のpersistence.xmlとおりです。

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter" />
    </property>
    <property name="persistenceUnitName" value="PersistenceUnitAppDeploy" />
    <property name="jpaProperties">
        <props>
          <prop key="openjpa.ConnectionURL">jdbc:postgresql:127.0.0.1:5432/database</prop>
          <prop key="openjpa.ConnectionUserName">user</prop>
          <prop key="openjpa.ConnectionPassword">user</prop>
          <prop key="openjpa.ConnectionDriverName">org.postgresql.Driver</prop>
        </props>
    </property>
</bean>

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="PersistenceUnitAppDeploy" transaction-type="RESOURCE_LOCAL">
        <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
        <class>devicemanage.repository.appdeploy.entity.Subtask</class>
        ... // ommit other entity declaration
        <properties>

            <property name="openjpa.RemoteCommitProvider" value="sjvm" />
            <property name="openjpa.QueryCache" value="true" />
            <property name="openjpa.RuntimeUnenhancedClasses" value="unsupported" />
            <property name="openjpa.InverseManager" value="true(Action=warn)" />

            <property name="openjpa.OrphanedKeyAction" value="log(Channel=Orphans, Level=TRACE)" />
            <property name="openjpa.ConnectionFactoryProperties" value="PrettyPrint=true, PrettyPrintLineLength=200" />
            <property name="openjpa.Multithreaded" value="true" />
            <property name="openjpa.LockManager" value="none" />
            <property name="openjpa.WriteLockLevel" value="none" />

            <property name="openjpa.Compatibility" value="QuotedNumbersInQueries=true" />

            <property name="openjpa.Log" value="DefaultLevel=TRACE, Runtime=TRACE, Tool=TRACE, SQL=TRACE, Query=TRACE" />
        </properties>
    </persistence-unit>
</persistence>

QueryCacheQuerySQLCacheの両方をfalseに設定しても、正しく動作しません。

<property name="openjpa.jdbc.QuerySQLCache" value="false" />
<property name="openjpa.QueryCache" value="false" />

ただし、openjpaライブラリをバージョン2.2.2にダウングレードすると、すべて同じコードと構成で正確に機能します。また、@Query以下のように subtaskDao インターフェースに を使用すると、 と一緒でもうまく動作しopenjpa version 2.4.1ます。

@Query("SELECT entity FROM Subtask as entity WHERE entity.deviceId IN :deviceIdList AND entity.state IN :states")
public List<Subtask> findByDeviceIdInAndStateInOrderByIdAsc(@Param("deviceIdList") Collection<String> deviceIdList, @Param("states")  Collection<Integer> states);

私が発見していないトリッキーは何ですか?

4

0 に答える 0