アップデート:
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 回目の呼び出しでは、cachedCriteriaQuery
isSELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id
とvalues
is [[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です。deviceId
state
1000000002
5
10
1000000003
5
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-jpa
、jdbc 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>
QueryCacheとQuerySQLCacheの両方を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);
私が発見していないトリッキーは何ですか?