Hibernate-reactive: Ошибка подстановки параметров при ограничении результатов на MS SQL Server и Hibernate Reactive 1.0.0.CR9

Созданный на 5 авг. 2021  ·  13Комментарии  ·  Источник: hibernate/hibernate-reactive

Здравствуйте, у меня возникла проблема с параметрами запроса MS SQL в 1.0.0.CR9 при использовании разбиения по страницам или диапазонов (исключение SQL, сообщение = 'Неверный синтаксис рядом с'? '.'), Возможно, из-за того, что параметр находится в предложении Select?

Стек: Quarkus 2.1.0.Final / Panache Reactive / Mutiny 4.1.2 / Hibernate Reactive 1.0.0.CR9 / Hibernate Core 5.5.5.Final

Из моего ресурса отдыха, запустив это:
[PanacheRepository].find("firstName LIKE ?1").page(Page.of(25)).list();

При входе в систему SQL в Hibernate, это произведенный SQL:

Hibernate:
    select
        top(?) request0_.id as id1_1_,
        request0_.caseId as caseid2_1_,
        request0_.caseNumber as casenumb3_1_,
        request0_.expires as expires4_1_,
        request0_.addressLine1 as addressl5_1_,
        request0_.addressLine2 as addressl6_1_,
        request0_.agency as agency7_1_,
        request0_.city as city8_1_,
        request0_.email as email9_1_,
        request0_.firstName as firstna10_1_,
        request0_.lastName as lastnam11_1_,
        request0_.phone as phone12_1_,
        request0_.relationship as relatio13_1_,
        request0_.state as state14_1_,
        request0_.zipcode as zipcode15_1_,
        request0_.submitted as submitt16_1_
    from
        Request request0_
    where
        request0_.firstName like <strong i="11">@P1</strong>

Обратите внимание на правильную подстановку параметра в предложении where, но для «top (?)» В предложении select подстановки не было.
Если я удалю разбиение на страницы и перезапущу, запрос будет выполнен успешно.

Ниже показано ведение журнала трассировки, показывающее HQL и SQL. Я предполагаю, что Panache использует setMaxResults() в запросе. Мы рады открыть проблему с Quarkus или Mutiny, если проблема именно в этом, и заранее благодарим вас!

2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-1) HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1
2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-1) SQL: select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like ?
2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.ErrorTracker] (vert.x-eventloop-thread-1) throwQueryException() : no errors
2021-08-05 12:55:57,342 TRACE [org.hib.rea.ses.imp.ReactiveHQLQueryPlan] (vert.x-eventloop-thread-1) Find: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1
2021-08-05 12:55:57,344 TRACE [org.hib.eng.spi.QueryParameters] (vert.x-eventloop-thread-1) Named parameters: {1=%j%}
2021-08-05 12:55:57,345 TRACE [org.hib.typ.des.sql.BasicBinder] (vert.x-eventloop-thread-1) binding parameter [2] as [VARCHAR] - [%j%]
2021-08-05 12:55:57,345 TRACE [org.hib.loa.Loader] (vert.x-eventloop-thread-1) Bound [3] parameters total
2021-08-05 12:55:57,355 DEBUG [org.hib.SQL] (vert.x-eventloop-thread-1)
    select
        top(?) request0_.id as id1_1_,
        request0_.caseId as caseid2_1_,
        request0_.caseNumber as casenumb3_1_,
        request0_.expires as expires4_1_,
        request0_.addressLine1 as addressl5_1_,
        request0_.addressLine2 as addressl6_1_,
        request0_.agency as agency7_1_,
        request0_.city as city8_1_,
        request0_.email as email9_1_,
        request0_.firstName as firstna10_1_,
        request0_.lastName as lastnam11_1_,
        request0_.phone as phone12_1_,
        request0_.relationship as relatio13_1_,
        request0_.state as state14_1_,
        request0_.zipcode as zipcode15_1_,
        request0_.submitted as submitt16_1_
    from
        Request request0_
    where
        request0_.firstName like <strong i="18">@P1</strong>
2021-08-05 12:55:57,358 ERROR [org.hib.rea.errors] (vert.x-eventloop-thread-1) HR000057: Failed to execute statement [$1select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like @P1]: $2could not execute query: java.util.concurrent.CompletionException: io.vertx.mssqlclient.MSSQLException: {number=102, state=1, severity=15, message='Incorrect syntax near '?'.', serverName='aa3ae4a0fd3c', lineNumber=1, additional=[io.vertx.mssqlclient.MSSQLException: {number=8180, state=1, severity=16, message='Statement(s) could not be prepared.', serverName='aa3ae4a0fd3c', lineNumber=1}]}

Все 13 Комментарий

В этом SQL есть что-то странное.

Почему мы даже используем top n вместо offset ... fetch ... . А зачем лишние круглые скобки?

Я предполагаю, что Panache использует setMaxResults() в запросе.

Ну, конечно, должно быть. У Panache нет бизнеса, и он не занимается собственной генерацией SQL, верно @FroMage?

Я попробовал это в простом HR и получил:

select hqlquerypa0_.id as id1_0_, hqlquerypa0_.description as descript2_0_, hqlquerypa0_.name as name3_0_, hqlquerypa0_.type as type4_0_ from Flour hqlquerypa0_ order by hqlquerypa0_.id offset <strong i="6">@P1</strong> rows fetch next <strong i="7">@P2</strong> rows only

Мне кажется, что это проблема, связанная с Panache.

Большое спасибо за обзор, и я обращусь к некоторым проектам в стеке. Поскольку единственный HQL, показанный в журналах трассировки, был FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 , я предположил, что добавление top(?) исходило из реализации MS SQL для setMaxResults() , однако SQL в вашем сообщении показывает, что он использует совершенно другой синтаксис.

По-прежнему сталкиваясь с проблемой, я попытался добавить заказ, чтобы получить этот HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 ORDER BY submitted

2021-08-05 16:20:37,992 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-19) HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 ORDER BY submitted
2021-08-05 16:20:37,992 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-19) SQL: select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like ? order by request0_.submitted

Это попыталось выполнить следующий SQL:

2021-08-05 16:20:38,022 DEBUG [org.hib.SQL] (vert.x-eventloop-thread-19)
    select
        request0_.id as id1_1_,
        request0_.caseId as caseid2_1_,
        request0_.caseNumber as casenumb3_1_,
        request0_.expires as expires4_1_,
        request0_.addressLine1 as addressl5_1_,
        request0_.addressLine2 as addressl6_1_,
        request0_.agency as agency7_1_,
        request0_.city as city8_1_,
        request0_.email as email9_1_,
        request0_.firstName as firstna10_1_,
        request0_.lastName as lastnam11_1_,
        request0_.phone as phone12_1_,
        request0_.relationship as relatio13_1_,
        request0_.state as state14_1_,
        request0_.zipcode as zipcode15_1_,
        request0_.submitted as submitt16_1_
    from
        Request request0_
    where
        request0_.firstName like <strong i="10">@P1</strong>
    order by
        request0_.submitted offset 0 rows fetch next ? rows only

Итак ... та же проблема все еще существует в том, что он не выполнил подстановку параметров должным образом (теперь для fetch next ? rows only ), но это изменило сгенерированный синтаксис SQL в соответствии с вашим примером.

Поскольку Quarkus еще не включает hibernate-reactive-core 1.0.0.CR9, я специально переопределил версию в моем pom для этого и hibernate-core 5.5.5.Final. Могу ли я упустить зависимость?

Я посмотрел, где, по моему мнению, запрос Hibernate создается в Panache io.quarkus.hibernate.reactive.panache.common.runtime.CommonPanache QueryImpl: L254 , setFirstResult и setMaxResults по запросу. Я посмотрел на объект запроса в отладчике, и в конце этого метода createQuery у базового ReactiveQueryImpl есть queryOptions, содержащие firstRow = 0 и maxRows = 25.

Цените помощь! (Поймите, я использую предварительный просмотр)

Я думаю, что нашел ошибку, вероятно, в SQLServerParameters.processLimit ()

Panache создает запрос гибернации с firstRow = 0.

Внутри org.hibernate.reactive.loader.ReactiveLoader.executeReactiveQueryStatement SQLServer2012LimitHandler.getOffsetFetch() :

if ( !LimitHelper.hasFirstRow( selection ) ) {
    return " offset 0 rows fetch next ? rows only";
}
return " offset ? rows fetch next ? rows only";

Следовательно, на этом этапе SQL заканчивается на offset 0 rows fetch next ? rows only . Обратите внимание на 0 вместо?.
От SQLServerParameters.processLimit() :

        if (isProcessingNotRequired(sql)) {
            return sql;
        }

        // Replace 'offset ? fetch next ? rows only' with the <strong i="17">@P</strong> style parameters for Sql Server
        int index = hasOffset ? parameterArray.length - 1 : parameterArray.length;
        int pos = sql.indexOf( " offset ?" );
        if ( pos > -1 ) {
            String sqlProcessed = sql.substring( 0, pos ) + " offset @P" + index++ + " rows";
            if ( sql.indexOf( " fetch next ?" ) > -1 ) {
                sqlProcessed += " fetch next @P" + index + " rows only ";
            }
            return sqlProcessed;
        }

        return sql;

sql.indexOf( " offset ?") - это проблема: поскольку LimitHandler уже жестко запрограммировал смещение на 0, подстрока offset ? не найдена, поэтому он не пытается обрабатывать какие-либо дополнительные параметры.
Следует ли вынести вложенное совпадение строки, ищущее fetch next ? за пределы совпадения для offset ?

@bharward Если бы вы могли создать тестовый пример, который использует только Hibernate Reactive, это было бы очень полезно

Я думаю, мы также рассмотрим тот, который использует Panache, но этот репозиторий не подходит для такого рода проблем.

Я подозреваю, что нам не хватает теста, который устанавливает только максимальные результаты, а не первый результат.

@gavinking и @DavideD - Спасибо. Похоже, что другой тест, который будет выполнять _both_ setFirstResult(0) и setMaxResults(n) (n - любое значение> 0), воспроизведет ошибку, которую я вижу.

SQLServer2012LimitHandler.getOffsetFetch() создает другой SQL, когда firstResult == 0 ("смещение 0 строк ...") по сравнению с тем, когда firstResult> 0 ("offset? Rows");

Если это все еще будет полезно, через несколько дней я мог бы потратить некоторое время на создание небольшого примера только для HR для воспроизведения или на изменение HQLQueryParameterNamedLimitTest, чтобы добавить тест, как я описал.

Ах, вы правы, этот тест не пройдёт через HQLQueryParameterNamedLimitTest :

    <strong i="7">@Test</strong>
    public void testFirstResultZeroAndMaxResults(TestContext context) {
        test(
                context,
                openSession()
                        .thenCompose( s ->
                                s.createQuery( "from Flour order by id" )
                                        .setFirstResult( 0 )
                                        .setMaxResults( 10 )
                                        .getResultList()
                                        .thenAccept( result -> {
                                            context.assertEquals( 3, result.size() );
                                        } )
                        )
        );
    }

я взгляну

@DavideD _ в дополнение к предложенному вами тесту, могу ли я также предложить дополнительный тест вроде этого:

       <strong i="7">@Test</strong>
    public void testFirstResultZeroAndMaxResultsWithoutOrderBy(TestContext context) {
        test(
                context,
                openSession()
                        .thenCompose( s ->
                                s.createQuery( "from Flour" )
                                        .setFirstResult( 0 )
                                        .setMaxResults( 10 )
                                        .getResultList()
                                        .thenAccept( result -> {
                                            context.assertEquals( 3, result.size() );
                                        } )
                        )
        );
    }

Отличие заключается в удалении «порядка по». Причина в том, что SQLServer2005LimitHandler записывает запрос как select top(?) ... всякий раз, когда запрос не включает «порядок по», но когда он это делает, он использует синтаксис offset ? fetch next ? . Поэтому я думаю, что нужны 2 тестовых примера.

Очень признателен!!

Была ли эта страница полезной?
0 / 5 - 0 рейтинги