Не удалось выполнить запрос с помощью методов запроса spring-data-jpa с суффиксом IN из-за неправильного указания cachedCriteriaQuery в openjpa 2.4.1

Обновить:

После того, как я покопался в исходном коде методов запроса репозитория spring-data-jpa, я обнаружил, что основной причиной является createQuery Метод PartTreeJpaQuery, как показано ниже.

Этот метод будет вызываться методом invoke объекта QueryExecutorMethodInterceptor объекта RepositoryFactorySupport при вызове интерфейса методов запроса репозитория spring-data-jpa.

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]] оба являются правильными, как и ожидалось.

Однако во время второго звонка cachedCriteriaQuery является SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id и values является [[1000000003], [5]], Похоже, что значение передается в OpenJPA и JPA запрос правильный, но criteriaQuery нет.

Если бы я изменил openjpa libs до версии 2.2.2, каждый раз при вызове этого метода cachedCriteriaQuery переменная ВСЕГДА SELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.id,

  • Почему cachedCriteriaQuery переменная будет изменена?

    Это частное и последнее поле QueryPreparer в PartTreeJpaQuery, и единственный способ назначить его - это конструктор QueryPreparer. Я там пометил точку останова, но между первым и вторым вызовом времени я не видел, чтобы какой-либо процесс вызывал этот конструктор, и как эта переменная могла измениться?

  • Все, что я сделал, это просто изменил библиотеки openjpa.

    Но приведенные выше коды - это spring-data-jpa и / или spring-data-commons. Как это может повлиять на поведение создания запроса?


Введите код модульного теста, как показано ниже, для запроса объекта подзадачи find IN deviceId а также state поля. Первый запрос - найти в ID устройства 1000000002 и государство 5 а также 10, Второй раз запрос найти в DeviceId 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());
    }
}

Подзадача Дао 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);
}

Тем не менее, результаты приведены ниже, которые не являются правильными, как мы ожидаем. Второй раз запрос вышел, результат является подзадачей deviceId 1000000002 вместо идентификатора устройства 1000000003,

Согласно openjpa.Log, в первый раз это запрос (в строке 7272) Query "SELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.id", Но во второй раз, это запрос 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 LIBS мы использовали следующим образом, и 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);

Что за хитрость я не обнаружил?

0 ответов

Другие вопросы по тегам