Есть ли способ повысить производительность запросов, генерируемых Hibernate, когда задействованы хранимые функции? - PullRequest
0 голосов
/ 26 февраля 2019

У меня есть страница, которая показывает некоторые записи в сетке в разбивке по страницам.У меня есть таблица my_table и сущность MyTable, связанная с ней:

@SuppressWarnings("serial")
@Entity
@Table(name="my_table")
@Inheritance(strategy=InheritanceType.JOINED)
public class MyTable extends BaseEntity implements Auditable, Serializable {
    //...
    private Integer myAttribute; //This does not exist in the table
    //...
    @Formula(value = "(myFunction(attr1, attr2))")
    public Integer getMyAttribute() {
        return myAttribute;
    }

    public void setMyAttribute(Integer myAttribute) {
        this.myAttribute = myAttribute;
    }
    //...
}

Когда я собираюсь выполнить запрос по "обычным" полям, все работает очень хорошо, но когда я пытаюсь фильтровать по myAttribute, например:

queryInput.addAndCriterion(Restrictions.eq("myAttribute", v));

, где, например, v - это Integer со значением 123, поиск будет остановлен.Если я запускаю хранимую функцию в MySQL напрямую, то она выполняется мгновенно.Я думаю, что этот код отправляет отдельный запрос на каждый элемент, который может объяснить проблему.Есть ли способ гарантировать, что я могу фильтровать по своей хранимой функции быстрым способом (возможно, вызов для хранимой функции будет сгенерирован в запросе)?Мне нужно будет определить критерий, который определяет, что для каждой записи должна вызываться определенная хранимая функция и передаются attr1 и attr2, которые являются полями записей?

1 Ответ

0 голосов
/ 26 февраля 2019

Как отмечено в комментарии ниже, этот ответ не решает проблему устранения неполадок в Hibernate, но ОП все равно понравился.

Ответ следует ...


Запросдля любой функции, хранимой или встроенной, всегда выполняется сканирование таблицы.

Например, при этом невозможно будет использовать индекс для create_date, даже если он существует:

SELECT * FROM MyTable WHERE MONTH(create_date) = 2

То же самое верно всякий раз, когда вы используете индексированный столбец в качестве аргументов функции.

Обходной путь для MySQL 5.7 и более поздних версий заключается в использовании сгенерированного столбца для этого выражения,и затем индексируйте сгенерированный столбец.

ALTER TABLE MyTable
  ADD COLUMN created_month INT AS (MONTH(create_date)),
  ADD INDEX (created_month);

Как только вы это сделаете, вы можете запросить created_month = 2 или даже запросить исходное выражение MONTH(create_date) = 2, и он будет использовать индекс.

К сожалению, вы можете использовать эту функцию только со встроенными функциями MySQL.

https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html говорит:

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

  • Сохраненные функции и пользовательские функции недопустимы.

Альтернативное решение будет для вассоздайте новый конкретный столбец для хранения результата сохраненной функции, предполагая, что значение является детерминированным по своим аргументам и не зависит от состояния данных в других таблицах.

ALTER TABLE MyTable
  ADD COLUMN myAttribute INT,
  ADD INDEX (myAttribute);

CREATE TRIGGER att_ins BEFORE INSERT ON MyTable 
FOR EACH ROW SET NEW.myAttribute = MyFunction(NEW.attr1, NEW.attr2);

CREATE TRIGGER att_upd BEFORE UPDATE ON MyTable 
FOR EACH ROW SET NEW.myAttribute = MyFunction(NEW.attr1, NEW.attr2);

Затем вы бы запросилиНовый столбец вместо выражения.

Это немного хлопотно, но это единственный способ получить индексированный поиск по результату сохраненной функции.

...