Hibernate-reactive: Parameterersetzungsfehler beim Einschränken der Ergebnisse auf MS SQL Server und Hibernate Reactive 1.0.0.CR9

Erstellt am 5. Aug. 2021  ·  13Kommentare  ·  Quelle: hibernate/hibernate-reactive

Hallo, ich habe ein Problem mit MS SQL-Abfrageparametern in 1.0.0.CR9 bei der Verwendung von Paging oder Bereichen (SQL-Ausnahme, message='Falsche Syntax in der Nähe von '?'.'), möglicherweise weil der Parameter in der Select-Klausel enthalten ist?

Stack: Quarkus 2.1.0.Final / Panache Reactive / Meuterei 4.1.2 / Hibernate Reactive 1.0.0.CR9 / Hibernate Core 5.5.5.Final

Von meiner Rest-Ressource aus, führe dies aus:
[PanacheRepository].find("firstName LIKE ?1").page(Page.of(25)).list();

Bei der SQL-Anmeldung im Ruhezustand ist dies die erzeugte 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>

Beachten Sie die korrekte Parameterersetzung in der where-Klausel, aber die Ersetzung von "top(?)" in der select-Klausel fand nicht statt.
Wenn ich das Paging entferne und erneut ausführe, wird die Abfrage erfolgreich ausgeführt.

Die unten gezeigte Ablaufverfolgungsprotokollierung zeigt HQL und SQL. Ich würde vermuten, dass Panache setMaxResults() für die Abfrage verwendet. Gerne eröffne ein Problem mit Quarkus oder Mutiny, wenn das Problem dort liegt, und danke im Voraus!

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}]}
bug

Alle 13 Kommentare

Diese SQL hat etwas Seltsames.

Warum verwenden wir sogar top n anstelle von offset ... fetch ... . Und warum die überflüssigen Klammern?

Ich würde vermuten, dass Panache setMaxResults() für die Abfrage verwendet.

Nun, es sollte auf jeden Fall sein. Panache hat kein Geschäft damit, seine eigene SQL-Generierung durchzuführen, oder @FroMage?

Nun, ich habe dies im einfachen HR versucht und bekomme:

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

Es scheint mir also, dass dies ein Problem im Zusammenhang mit Panache sein muss.

Vielen Dank für die Überprüfung und ich werde auf einige der Projekte im Stapel eingehen. Da die einzige in den Ablaufverfolgungsprotokollen angezeigte HQL FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 , nahm ich an, dass das Hinzufügen von top(?) aus der MS SQL-Implementierung für setMaxResults() , jedoch die SQL in Ihrem Beitrag zeigt, dass es eine völlig andere Syntax verwendet.

Da ich immer noch auf das Problem stoße, habe ich versucht, eine Bestellung hinzuzufügen, um diese HQL zu erhalten: 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

Dies hat versucht, die folgende SQL auszuführen:

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

Das gleiche Problem besteht also immer noch darin, dass die Parameterersetzung nicht richtig ausgeführt wurde (jetzt für fetch next ? rows only ), aber dies hat die generierte SQL-Syntax geändert, damit sie Ihrem Beispiel entspricht.

Da Quarkus hibernate-reactive-core 1.0.0.CR9 noch nicht enthält, habe ich ausdrücklich die Version in meinem Pom dafür und hibernate-core 5.5.5.Final überschrieben. Könnte mir eine Abhängigkeit fehlen?

Ich habe mir angeschaut, wo meiner Meinung nach die Hibernate-Abfrage in Panache io.quarkus.hibernate.reactive.panache.common.runtime.CommonPanache QueryImpl:L254 erstellt wird. Sie scheint nur setFirstResult und setMaxResults auf die Abfrage. Ich habe mir das Abfrageobjekt im Debugger angesehen und am Ende dieser createQuery Methode hat das zugrunde liegende ReactiveQueryImpl queryOptions mit firstRow=0 und maxRows=25.

Schätzen Sie die Hilfe! (Verstehe, dass ich eine Vorschau verwende)

Ich glaube, ich habe einen Fehler gefunden, wahrscheinlich in SQLServerParameters.processLimit()

Panache erstellt eine Ruhezustandsabfrage mit firstRow=0.

Innerhalb von org.hibernate.reactive.loader.ReactiveLoader.executeReactiveQueryStatement wird SQLServer2012LimitHandler.getOffsetFetch() aufgerufen:

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

Daher endet die SQL an dieser Stelle mit offset 0 rows fetch next ? rows only . Beachten Sie die 0 anstelle des ?.
Ab 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;

Das sql.indexOf( " offset ?") ist das Problem: Da der LimitHandler den Offset bereits auf 0 hartcodiert hat, wird der Teilstring offset ? nicht gefunden und versucht daher keine weiteren Parameter zu verarbeiten.
Soll der verschachtelte String-Match, der nach fetch next ? sucht, außerhalb des Matches für offset verschoben werden?

@bharward Wenn Sie einen Testfall erstellen könnten, der nur Hibernate Reactive verwendet, würden Sie das sehr schätzen

Ich denke, wir werden uns auch eines ansehen, das Panache verwendet, aber dieses Repository ist nicht der richtige Ort für solche Probleme

Ich vermute, dass uns ein Test fehlt, der nur maximale Ergebnisse und nicht das erste Ergebnis festlegt.

@gavinking und @DavideD - Danke. Es scheint, dass ein anderer Test, der _sowohl_ setFirstResult(0) als auch setMaxResults(n) (wobei n ein beliebiger Wert > 0 ist), den Fehler reproduzieren würde, den ich sehe.

SQLServer2012LimitHandler.getOffsetFetch() erzeugt eine andere SQL, wenn firstResult==0 ("offset 0 rows...") im Vergleich zu firstResult >0 ("offset ? rows");

Wenn es immer noch hilfreich wäre, könnte ich in ein paar Tagen etwas Zeit damit verbringen, entweder ein kleines reines HR-Beispiel zu erstellen, um es zu reproduzieren, oder HQLQueryParameterNamedLimitTest zu ändern, um einen Test wie beschrieben hinzuzufügen.

Ah, Sie haben Recht, dieser Test wird in HQLQueryParameterNamedLimitTest fehlschlagen:

    <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() );
                                        } )
                        )
        );
    }

ich werde nachsehen

@DavideD _zusätzlich_ zu dem von Ihnen vorgeschlagenen Test, darf ich auch einen zusätzlichen Test wie diesen vorschlagen:

       <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() );
                                        } )
                        )
        );
    }

Der Unterschied besteht darin, das "Bestellen nach" zu entfernen. Der Grund dafür ist , dass SQLServer2005LimitHandler die Abfrage wie schreibt select top(?) ... , wenn die Abfrage nicht ein „order by“ enthält, aber wenn es funktioniert verwendet er die offset ? fetch next ? Syntax. Daher denke ich, dass 2 Testfälle erforderlich sind.

Sehr geschätzt!!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen