Hibernate-reactive: 限制 MS SQL Server 和 Hibernate Reactive 1.0.0.CR9 上的结果时出现参数替换错误

创建于 2021-08-05  ·  13评论  ·  资料来源: hibernate/hibernate-reactive

您好,我在使用分页或范围时遇到了 1.0.0.CR9 中的 MS SQL 查询参数问题(SQL 异常,message='Incorrect syntax near '?'.'),可能是因为该参数在 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();

在 Hibernate 中登录 SQL 后,这是生成的 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 子句中的正确参数替换,但是 select 子句中的“top(?)”没有发生替换。
如果我删除分页并重新运行,查询将成功执行。

跟踪日志如下所示,显示 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

所以在我看来,这一定是一个与华丽相关的问题。

非常感谢您的审阅,我会联系堆栈中的一些项目。 由于跟踪日志中显示的唯一 HQL 是FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 ,我假设top(?)来自setMaxResults()的 MS SQL 实现,但是您帖子中的 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,我专门为此和 hibernate-core 5.5.5.Final 覆盖了我的 pom 中的版本。 我可能缺少依赖项吗?

我查看了我认为在 Panache io.quarkus.hibernate.reactive.panache.common.runtime.CommonPanache QueryImpl:L254 中创建 Hibernate 查询的setFirstResultsetMaxResults在查询上。 我查看了调试器中的查询对象,在createQuery方法的末尾,底层的 ReactiveQueryImpl 具有包含 firstRow=0 和 maxRows=25 的 queryOptions。

感谢帮助! (了解我正在使用预览)

我想我发现了一个错误,可能在SQLServerParameters.processLimit()

Panache 正在创建一个 firstRow=0 的休眠查询。

org.hibernate.reactive.loader.ReactiveLoader.executeReactiveQueryStatementSQLServer2012LimitHandler.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 的,但是这个存储库不是解决这类问题的正确位置

我怀疑我们错过了一个只设置最大结果而不是第一个结果的测试。

我们有一个基本的: https :

@gavinking@DavideD - 谢谢。 似乎另一个测试,一个可以执行_both_ setFirstResult(0)setMaxResults(n) (n 是任何值 > 0)的测试,会重现我看到的错误。

SQLServer2012LimitHandler.getOffsetFetch()在 firstResult==0 ("offset 0 rows...") 和 firstResult >0 ("offset ? rows") 时创建不同的 SQL;

如果它仍然有帮助,那么几天后我可以花一些时间创建一个仅 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() );
                                        } )
                        )
        );
    }

区别在于删除“order by”。 其原因是, SQLServer2005LimitHandler写入查询作为select top(?) ...每当查询包括“按订单”,但是当它它使用offset ? fetch next ?语法。 所以我认为需要2个测试用例。

非常感激!!

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

gavinking picture gavinking  ·  16评论

DavideD picture DavideD  ·  37评论

pqab picture pqab  ·  21评论

DavideD picture DavideD  ·  31评论

murphye picture murphye  ·  40评论