entityManager.createQuery()需要花费大量时间来构建查询和绑定参数。效果受到影响

问题描述 投票:0回答:1

我们正在使用Spring JPA条件查询(javax.persistence.criteria.CriteriaQuery)从数据库中获取数据。我们使用javax.persistence.criteria.Predicate来构建谓词。一个查询中有1500个“或”谓词。并且每个谓词都有6个“ AND”谓词。

SELECT (*) FROM TABLE_ABC as T1 WHERE  (t1.column1 = 'c11' AND
   t1.column2 = 'c12' AND t1.column3 = 'c13' AND t1.column4 = 'c14' AND
   t1.column5 = 'c15') 
  OR 
   (t1.column1 = 'c21' AND t1.column2 = 'c22'
   AND t1.column3 = 'c23' AND t1.column4 = 'c24' AND t1.column5 = 'c25')
   OR 
    (t1.column1 = 'c31' AND t1.column2 = 'c32'
   AND t1.column3 = 'c33' AND t1.column4 = 'c34' AND t1.column5 = 'c35').....

以前我们使用的是“ org.hibernate.Criteria”,并使用“ Conjuction”和“ Disjunction”来构建相同的查询。这种方法有效地工作。随着对“ org.hibernate.Criteria”的描述,我们将移至javax-criteriaquery软件包。我们正面临着性能的严重下降。日志的向下钻取表明在步骤

中消耗了更多时间

=> entityManager.createQuery(),它执行以下操作


  1. CriteriaCompiler.compile
  2. CriteriaQueryImpl $ 1.buildCompiledQuery
  3. CriteriaCompiler $ 1 $ 1.bind

这些操作比较耗时。

有什么解决方案可以使这些执行速度更快?'javax.persistence.criteria.CriteriaQuery'是前进的方向吗?

请在这里帮助!

请参见下面的代码:

@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
public getData(List<DataDAO> dataReqList) {
{

    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<DataReq> criteriaQuery = builder.createQuery(DataReq.class);
    Root<DataReq> dataReqRoot = criteriaQuery.from(DataReq.class);
    Predicate[] predicateArr = new Predicate[dataReqList.size()];

    for (DataDAO dataReq : dataReqList) {

                    predicateArr[i] = builder.and(
                            builder.equal(dataReqRoot.get(TEST_S), dataReq.getS()),
                            builder.equal(dataReqRoot.get(TEST_T2), dataReq.getT2()),
                            builder.equal(dataReqRoot.get(K1), dataReq.getK1()),
                            builder.equal(dataReqRoot.get(K2), dataReq.getK2()),
                            builder.equal(dataReqRoot.get(TEST_P), dataReq.getP()),
                            builder.equal(dataReqRoot.get(TEST_T1),
                                    dataReq.getT1(),
                            builder.equal(dataReqRoot.get(TEST_I), dataReq.getI()));

                            i++;
    }

    List<Data> dataResultList = getResultList(builder, criteriaQuery, predicateArr);

}

private List<Data> getResultList(CriteriaBuilder builder,
            CriteriaQuery<DataReq> criteriaQuery, Predicate[] predicateArr) {
    criteriaQuery.where(builder.or(predicateArr));
    TypedQuery<DataReq> query = entityManager.createQuery(criteriaQuery);

    List<DataReq> dataReqList = null;
    try {

        dataReqList = query.getResultList();
    } catch(Exception e) {
    ...
    }

    return convertToData(dataReqList);

}

使用“ org.hibernate.Criteria”并使用'Conjuction'和'Disjunction'的同一查询在毫秒内非常有效。

java spring-data-jpa hibernate-criteria criteria-api criteriaquery
1个回答
0
投票

对于上下文,取决于您使用的数据库,这就像带有行值表达式的动态IN谓词。如果支持,您还可以编写:

WHERE (t1.column1, t1.column2, t1.column3, t1.column4, t1.column5, t1.column6) IN (
  ('c11', 'c12', 'c13', 'c14', 'c15', 'c16'),
  ('c21', 'c22', 'c23', 'c24', 'c25', 'c26'),
  ...
)

这么长的IN列表不仅会在生成动态SQL的客户端库中产生问题,还会在服务器端产生问题。您提到绑定变量,也许您使用的旧API毕竟没有使用绑定变量,而是将所有值内联到查询中。我已经看到,在大型参数集中,Oracle的性能要好得多,因此,这是inline values might be better than bind variables的情况之一。

由于您正在使用Hibernate,因此可以尝试启用

<property name="hibernate.criteria.literal_handling_mode" value="bind"/>

请参见HHH-9576this answer

使用数组可能甚至更好的解决方案

以上内容(可能)有助于恢复以前的性能,但是根据IN列表的大小,可能会有更好的解决方案。我在博客上写了一个可以使用arrays instead of individual bind values, in case you're using Oracle or PostgreSQL的替代方法。

使用临时表的一种甚至更好的解决方案

我经常看到的另一个选择是使用以下形式的临时表(假设Oracle):

CREATE GLOBAL TEMPORARY TABLE predicates (
  column1 VARCHAR2(100),
  column2 VARCHAR2(100), 
  column3 VARCHAR2(100), 
  column4 VARCHAR2(100), 
  column5 VARCHAR2(100), 
  column6 VARCHAR2(100)
)

然后,在运行查询之前,将所有各种谓词值批量插入该表中,然后将其半联接:

WHERE (t1.column1, t1.column2, t1.column3, t1.column4, t1.column5, t1.column6) IN (
  SELECT column1, column2, column3, column4, column5, column6
  FROM predicates
)

如果没有临时表,则可以尝试使用普通表,并向其中添加一个transaction_id列,在查询后手动清除其内容。

© www.soinside.com 2019 - 2024. All rights reserved.