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变量 willSELECT s FROM Subtask s WHERE (s.deviceId IN (:deviceIdList) AND s.state IN (:states)) ORDER BY s.idvalues变量 is[[1000000002], [5, 10]]都如预期的那样正确。

但是,在第二次调用时,cachedCriteriaQueryisSELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id和 the valuesis [[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 的构造函数。我已经在那里设置了一个断点,但是在第一次和第二次调用之间,我没有看到任何进程调用了这个构造函数,这个变量怎么会改变?

  • 我所做的只是更改了 openjpa 库。

    但上面的代码是 spring-data-jpa 和/或 spring-data-commons。这如何影响查询创建的行为?


给出如下单元测试代码,通过 and 字段查询子find IN任务deviceId实体state。第一次查询是 find IN deviceId 1000000002and, state 5and 10。第二次查询是 find IN deviceId1000000003和 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());
    }
}

subtaskDaospring-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而不是 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"。但在第二次,它查询Query "SELECT s FROM Subtask s WHERE (s.deviceId IN ('1000000002') AND s.state IN (5,10)) ORDER BY s.id". 为什么参数不是:deviceIdListand :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和libs的版本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为 version 2.2.2它完全适用于所有相同的 code 和 configuration。此外,如果我使用@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