Конфигурация: 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, вы используете Hibernate 6 или более позднюю версию и Firebird 2.5 или более позднюю версию. Это ошибка в org.hibernate.community.dialect.FirebirdDialect
. Я посмотрю, смогу ли я это исправить. Если вы используете Firebird 3.0 или выше, диалект должен использовать стандартный синтаксис SQL OFFSET/FETCH.
@Mark Rotteveel - Да, я использую Hibernate 6 и Firebird 2.5 (сейчас я не могу изменить версию Firebird). Было бы здорово, если бы вы исправили эту ошибку. Ждем хороших новостей.
Я не на 100 % уверен, что Hibernate примет исправление для Firebird 2.5, поскольку срок службы этой версии истек с 2019 года. Какую именно версию Hibernate вы используете, поэтому я могу предоставить вам хотя бы обходной путь с собственный диалект (я не уверен на 100%, было ли много изменений в диалекте между Hibernate 6.0 и 6.5, но лучше перестраховаться, чем потом сожалеть :).
Это спящий режим 6.5.
В качестве обходного пути вы можете определить в своем проекте собственный диалект 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.
Большое спасибо. Ваша помощь неоценима.
Какую версию Hibernate вы используете и какую версию Firebird? А вы настроили Hibernate для использования диалекта Firebird?