Сначала сбивается диалект Firebird Hibernate? и пропустить? параметры в операторе

Конфигурация: Firebird 2.5, Hibernate 6.2 или 6.5.

Когда я вызываю собственный метод в PagingAndSortingRepository с объектом Pageable, для которого установлено значение page > 0, сгенерированный оператор сначала теряет место и пропускает параметры (они вставляются с помощью Spring + Hibernate), поэтому я не имею над ними никакого контроля.

Пример:

-- Контроллер

Pageable paging = PageRequest.of(1, 10);
Page<MaterialBrand> items = repository.findByMaterialSizesQuery(25, paging);

-- Репозиторий

@Query(
value = "select t_m_brands.id...... where gmin <= :size and gmax >= :size",
nativeQuery = true
)
Page<MaterialBrand> findByMaterialSizesQuery(@Param("size") Float size, Pageable pageable);

Вывод журнала: Спящий режим: выбирать пропускать ? первый ? t_m_brands.id,....

Что приводит к ошибке: Произошла непредвиденная ошибка (тип = внутренняя ошибка сервера, статус = 500). не удалось подготовить оператор [Ошибка динамического SQL; Код ошибки SQL = -104; Токен неизвестен – строка 1, столбец 21; ? [SQLState: 42000, код ошибки ISC: 335544634]]

Конечно, правильно сгенерированный оператор должен выглядеть так

Спящий режим: выбирать первый ? пропускать ? t_m_brands.id,....

Я должен добавить, что когда я использую стандартные методы, например репозиторий.findAll(paging), все в порядке, и первый и пропуск находятся в правильных позициях.

Надеюсь на любые решения.

Пытался реализовать QueryRewriter с репозиторием, но запрос в методе перезаписи все еще не является подготовленным оператором, поэтому никакого эффекта.

Какую версию Hibernate вы используете и какую версию Firebird? А вы настроили Hibernate для использования диалекта Firebird?

Mark Rotteveel 29.05.2024 19:51

Судя по коду Hibernate, вы используете Hibernate 6 или более позднюю версию и Firebird 2.5 или более позднюю версию. Это ошибка в org.hibernate.community.dialect.FirebirdDialect. Я посмотрю, смогу ли я это исправить. Если вы используете Firebird 3.0 или выше, диалект должен использовать стандартный синтаксис SQL OFFSET/FETCH.

Mark Rotteveel 29.05.2024 20:05

@Mark Rotteveel - Да, я использую Hibernate 6 и Firebird 2.5 (сейчас я не могу изменить версию Firebird). Было бы здорово, если бы вы исправили эту ошибку. Ждем хороших новостей.

Zadra 30.05.2024 07:09

Я не на 100 % уверен, что Hibernate примет исправление для Firebird 2.5, поскольку срок службы этой версии истек с 2019 года. Какую именно версию Hibernate вы используете, поэтому я могу предоставить вам хотя бы обходной путь с собственный диалект (я не уверен на 100%, было ли много изменений в диалекте между Hibernate 6.0 и 6.5, но лучше перестраховаться, чем потом сожалеть :).

Mark Rotteveel 30.05.2024 11:30

Это спящий режим 6.5.

Zadra 31.05.2024 07:04
1
5
98
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В качестве обходного пути вы можете определить в своем проекте собственный диалект Firebird, чтобы переопределить его реализацию LimitHandler и обеспечить правильный вывод:

package nl.lawinegevaar.hb.custom;

import org.hibernate.community.dialect.FirebirdDialect;
import org.hibernate.dialect.pagination.AbstractLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.query.spi.Limit;

public class CustomFirebirdDialect extends FirebirdDialect {

    @Override
    public LimitHandler getLimitHandler() {
        return FirstSkipLimitHandler.INSTANCE;
    }

    private static class FirstSkipLimitHandler extends AbstractLimitHandler {

        private static final LimitHandler INSTANCE = new FirstSkipLimitHandler();

        @Override
        public String processSql(String sql, Limit limit) {
            boolean hasFirstRow = hasFirstRow(limit);
            boolean hasMaxRows = hasMaxRows(limit);

            if (!hasFirstRow && !hasMaxRows) {
                return sql;
            }

            StringBuilder firstSkip = new StringBuilder();

            if (hasMaxRows) {
                firstSkip.append(" first ?");
            }
            if (hasFirstRow) {
                firstSkip.append(" skip ?");
            }

            return insertAfterSelect(firstSkip.toString(), sql);
        }

        @Override
        public final boolean supportsLimit() {
            return true;
        }

        @Override
        public boolean supportsOffset() {
            return true;
        }

        @Override
        public final boolean bindLimitParametersFirst() {
            return true
        }

        @Override
        public boolean bindLimitParametersInReverseOrder() {
            return true;
        }

        @Override
        public final boolean supportsVariableLimit() {
            return true;
        }
    }
}

Измените конфигурацию Hibernate, чтобы использовать этот собственный диалект вместо org.hibernate.community.dialect.FirebirdDialect.

Не стесняйтесь использовать его (я бы предложил использовать собственное имя пакета;). Поскольку он является производным от Hibernate, его лицензия — LGPL 2.1.

Глядя на историю коммитов Hibernate, кажется, что она была сломана во время рефакторинга, выполненного для Hibernate 6.0, из-за повторного использования обработчика, который генерирует skip ? first ? вместо того, который изначально был в диалекте Hibernate 5 и старше для генерации first ? skip ?.

Кроме того, во время написания тестов для подтверждения работоспособности моего решения я обнаружил, что Hibernate генерирует правильный запрос при использовании запросов JPQL/HQL (поскольку он использует другой метод добавления смещения и ограничения), но неправильный запрос при добавлении ограничения и ограничения. смещение к собственному запросу. Таким образом, другим обходным решением было бы не использовать собственный запрос, а вместо этого сделать его запросом JPQL.

Я сообщил об этом в Hibernate как о проблеме HHH-18213 и создал запрос на извлечение для Hibernate 6.5. Этот запрос на включение принят и должен быть выпущен вместе с Hibernate 6.5.3.

Большое спасибо. Ваша помощь неоценима.

Zadra 01.06.2024 19:00

Другие вопросы по теме