entityManager.createQuery () требует много времени для создания запроса и привязки параметров. Затронуто производительность

Мы используем запрос критериев 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-criterionquery. Мы столкнулись с большим падением производительности. Детализация журналов показывает, что на шаге тратится больше времени.

=> entityManager.createQuery (), который выполняет следующие операции


  1. CriteriaCompiler.compile
  2. CriteriaQueryImpl $ 1.buildCompiledQuery
  3. CriteriaCompiler $ 1 $ 1. привязать

Эти операции требуют больше времени.

Есть ли какое-нибудь решение, чтобы ускорить их выполнение? Является ли 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 работает очень эффективно за миллисекунды.


person Sreeram    schedule 27.03.2020    source источник
comment
Привет, Саймон, спасибо за интерес. У нас есть такой бизнес-сценарий. Тот же запрос с org.hibernate.Criteria и с использованием Conjunction и Disjunction работает довольно эффективно.   -  person Sreeram    schedule 28.03.2020


Ответы (1)


Для контекста, в зависимости от используемой базы данных, это похоже на динамический предикат 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 для больших наборов параметров, поэтому это один из случаев, когда встроенные значения могут быть лучше, чем привязанные переменные.

Поскольку вы используете Hibernate, вы можете попробовать включить

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

См. HHH-9576 и этот ответ

Возможно, даже лучшее решение с использованием массивов

Вышеупомянутое (возможно) поможет восстановить предыдущую производительность, которую вы испытали, но в зависимости от размера вашего IN списка могут быть даже лучшие решения. Я писал об альтернативе, где вы могли бы использовать массивы вместо отдельных значений привязки, если вы используете Oracle или 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, очищая его содержимое вручную после ваших запросов.

person Lukas Eder    schedule 29.03.2020