Failed to query by spring-data-jpa query methods w

2019-07-24 21:16发布

Update:

After I digged into the source code of spring-data-jpa repository query methods, I found the root cause is the createQuery method of PartTreeJpaQuery as below.

This method will be called by the invoke method of the QueryExecutorMethodInterceptor of the RepositoryFactorySupport when you call the spring-data-jpa repository query methods interface.

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

At the first time call, the cachedCriteriaQuery variable will be SELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.id and the values variable is [[1000000002], [5, 10]] which are both correct as expected.

However, at the second time call, the cachedCriteriaQuery is SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id and the values is [[1000000003], [5]]. Looks like the value pass into openjpa and jpa query is correct but the criteriaQuery isn't.

If I changed the openjpa libs to version 2.2.2, each time of calling this method, the cachedCriteriaQuery variable are ALWAYS SELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.id.

  • Why the cachedCriteriaQuery variable will be changed?

    It is a private and final field of QueryPreparer of PartTreeJpaQuery and the only way can assign it shall be the constructor of QueryPreparer. I have togged a breakpoint there, but between the first and second time call, I haven't seen any process has called this constructor and how can this variable became changed?

  • All I have done is just changed the openjpa libs.

    But above codes are spring-data-jpa and/or spring-data-commons. How can this affect the behavior of the Query creation?


Give the unit test code as below to query the subtask entity by find IN the deviceId and state fields. The first time query is find IN deviceId 1000000002 and, state 5 and 10. The second time query is find IN deviceId 1000000003 and state 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());
    }
}

The subtaskDao is spring-data-jpa repository query method interface as below

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

However the results are as below which are not correct as we expect. The second time query has come out the result is the subtask of deviceId 1000000002 instead of deviceId 1000000003.

According to the openjpa.Log, at the first time it query(at line 7272) Query "SELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.id". But at the second time, it query Query "SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id". Why the parameter are not :deviceIdList and :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

The version of spring, spring-data-jpa, jdbc driver and openjpa libs we used as follows, and the jdk is 1.8, the database is 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>

The entity classes are enhanced at the build time by 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> 

The EntityManagerFactoryBean settings and persistence.xml are as below

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

Even if I have set both the QueryCache and QuerySQLCache to false, it still not works right.

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

However, if I downgrade the openjpa libs to version 2.2.2, it works well exactly with all the same code and configuration. Moreover, if I used the @Query for the subtaskDao interface as below, it also works well even with 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);

What is the tricky I haven't discover?

0条回答
登录 后发表回答