like-Statement вызывает неподдерживаемый запрос в Ignite 2.7 с QueryDSL - PullRequest
0 голосов
/ 21 марта 2019

В приложении с весенней загрузкой мы используем queryDSL для доступа к базе данных. Приложение должно распечатать все элементы из таблицы, которые соответствуют (зависящим от пользователя) параметрам поиска.

Примеры:

  • Дайте мне все места с названием, которое точно соответствует "Берлин"
  • Дайте мне все места с названием, начинающимся с "Бер".

Таким образом, мы должны динамически создать предложение where.

Мы используем класс сущностей, такой как

package example;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "location")
public class LocationEntity {

    @Id
    @GeneratedValue
    @Column(name = "id", nullable = false)
    private Long id;

    @Column(name = "name", nullable = false)
    private String name;

    public LocationEntity() {
        // -
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

класс запросов типа

package example;

import com.querydsl.core.types.dsl.EntityPathBase;
import com.querydsl.core.types.dsl.NumberPath;
import com.querydsl.core.types.dsl.StringPath;

public class QLocationEntity extends EntityPathBase<LocationEntity> {
    private static final long serialVersionUID = 1L;

    public static final QLocationEntity DEFAULT = new QLocationEntity("loc_1");

    public final NumberPath<Long> id = createNumber("id", Long.class);

    public final StringPath name = createString("name");

    public QLocationEntity(String tableAlias) {
        super(LocationEntity.class, tableAlias);
    }
}

класс хранилища, такой как

package example;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface LocationRepository extends JpaRepository<LocationEntity, Long>,
        /* needed for query DSL. */
        QuerydslPredicateExecutor<LocationEntity> {
    /*
     * We don't need custom methods.
     */
}

и маленький вспомогательный класс типа

package example;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.BooleanExpression;

@Service
public class LocationRepositoryHelper {
    protected final Log LOG = LogFactory.getLog(getClass());

    @Autowired
    private LocationRepository repository;

    public Sort sort = new Sort(Sort.Direction.ASC, "name");

    public Iterable<LocationEntity> findEntities(String paramLocation, boolean autocomplete)
            throws RequestParameterInvalideException {
        Predicate p = generatePredicate(paramLocation, autocomplete);
        if (p != null) {
            return repository.findAll(p, sort);
        } else {
            return repository.findAll(sort);
        }
    }

    BooleanExpression generatePredicate(String location, boolean autocomplete) {
        if ("".equals(location.trim())) {
            return null;
        } else if (autocomplete) {
            return QLocationEntity.DEFAULT.name.startsWith(location);
        } else {
            return QLocationEntity.DEFAULT.name.eq(location);
        }
    }
}

(Этот пример гораздо менее сложен, чем наш реальный код приложения, но этого должно быть достаточно, чтобы продемонстрировать нашу проблему.)

Наше приложение запрашивает у класса помощника список, а весенняя загрузка должна делать все остальное, используя магию вуду.

Когда мы используем базу данных oracle в качестве rdbms, все в порядке. Когда мы используем воспламенение, все в порядке, пока мы не пытаемся использовать «автозаполнение».

Когда мы пытаемся «автозаполнить строку поиска», мы получаем IgniteException, которое говорит: неподдерживаемый запрос: locationen0_.name вроде? 1 ESCAPE '!'

Мы записали следующую строку sql:

select locationen0_.id as id1_0_, locationen0_.name as name2_0_ from my_location locationen0_ where locationen0_.name like ? escape '!' order by locationen0_.name asc

Мы предполагаем "?" должен быть заменен на «Ber%», поэтому полное определение SQL должно быть:

select locationen0_.id as id1_0_, locationen0_.name as name2_0_ from my_location locationen0_ where locationen0_.name like 'Ber%' escape '!' order by locationen0_.name asc;

Мы выполняли этот оператор вручную в консоли SQL на БД Oracle (12. *) и на Ignite (2.7). В Oracle все было хорошо, Ignite все еще объявлял, что у нас будет синтаксическая ошибка / неподдерживаемый запрос. Итак, мы попробовали несколько альтернатив на Ignite ...

select locationen0_.id as id1_0_, locationen0_.name as name2_0_ from my_location locationen0_ where locationen0_.name = 'Berlin' order by locationen0_.name asc;
=> all fine, but doesn't return what we want.

select locationen0_.id as id1_0_, locationen0_.name as name2_0_ from my_location locationen0_ where locationen0_.name like 'Berlin' order by locationen0_.name asc;
=> all fine, but still doesn't return what we want.

select locationen0_.id as id1_0_, locationen0_.name as name2_0_ from my_location locationen0_ where locationen0_.name = 'Ber%' order by locationen0_.name asc;
=> still all fine and would return what we want.

select locationen0_.id as id1_0_, locationen0_.name as name2_0_ from my_location locationen0_ where locationen0_.name = 'Berlin' escape '!' order by locationen0_.name asc;
=> all fine (woot??), but wouldn't return what we want.

select locationen0_.id as id1_0_, locationen0_.name as name2_0_ from my_location locationen0_ where locationen0_.name = 'Ber%' escape '!' order by locationen0_.name asc;
=> (the original statement) unsupported query, but is what queryDSL (supposedly) generates and what should return what we want.

Наш первый вывод:

  1. Ignite знает ключевое слово ESCAPE; использование ключевого слова не вызывает проблем во всех случаях.
  2. Ignite понимает подобные предложения с "%" внутри.
  3. Ignite не принимает подобные предложения с "%" внутри в сочетании с ключевым словом escape.

После нескольких часов анализа мы теперь знаем, «что вызывает проблему», но мы не знаем, почему это проблема вообще. Платформа queryDSL (версия 4.2.1) содержит ключевое слово escape, закодированное жестко, поэтому мы не имеем ни малейшего понятия, как его подавить. Переключение на другую инфраструктуру может быть вариантом, хотя мы бы хотели избежать рефакторинга. Работать с дампом фреймворка и «строить оператор путем конкатенации строк», но невозможно для производительного кода.

Итак, наши вопросы: Кто-нибудь использует queryDSL и Ignite и не имеет этой проблемы? Если да, то используете ли вы queryDSL совершенно иначе, чем мы? (Используем ли мы queryDSL способом «он не предназначен для использования»?) Или вы знаете вариант конфигурации для Ignite, который решает проблему? У кого-нибудь еще есть подсказка?

1 Ответ

0 голосов
/ 21 марта 2019

Я исследовал источник, и оказалось, что Ignite явно запрещает ESCAPE. Мы проверяем, указан ли ESCAPE, и указываем ошибку, если она есть.

Я думаю, что вы можете подать заявку на Apache Ignite JIRA.

...