Frühlingsdaten und native Abfrage mit Paginierung

80

In einem Webprojekt, das die neuesten Spring-Daten (1.10.2) mit einer MySQL 5.6-Datenbank verwendet, versuche ich, eine native Abfrage mit Paginierung zu verwenden, aber org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodExceptionbeim Start tritt eine auf.

UPDATE : 20180306 Dieses Problem wurde jetzt in Spring 2.0.4 behoben. Für diejenigen, die noch interessiert sind oder mit älteren Versionen nicht weiterkommen, überprüfen Sie die entsprechenden Antworten und Kommentare auf Problemumgehungen.

Gemäß Beispiel 50 unter Verwenden von @Query aus der Spring-Data-Dokumentation ist dies möglich, indem die Abfrage selbst und eine countQuery wie folgt angegeben werden:

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

Aus Neugier kann NativeJpaQueryich in der Klasse sehen, dass es den folgenden Code enthält, um zu überprüfen, ob es sich um eine gültige jpa-Abfrage handelt:

public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, EvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) {
   super(method, em, queryString, evaluationContextProvider, parser);
   JpaParameters parameters = method.getParameters();
   boolean hasPagingOrSortingParameter = parameters.hasPageableParameter() || parameters.hasSortParameter();
   boolean containsPageableOrSortInQueryExpression = queryString.contains("#pageable") || queryString.contains("#sort");
   if(hasPagingOrSortingParameter && !containsPageableOrSortInQueryExpression) {
       throw new InvalidJpaQueryMethodException("Cannot use native queries with dynamic sorting and/or pagination in method " + method);
   }
}

Meine Abfrage enthält einen PageableParameter, so hasPagingOrSortingParameterist true, aber es ist auch für eine Suche #pageableoder #sortSequenz innerhalb der queryString, die ich nicht bieten.

Ich habe versucht, #pageableam Ende meiner Abfrage etwas hinzuzufügen (es ist ein Kommentar), wodurch die Validierung bestanden wird. Bei der Ausführung schlägt jedoch fehl, dass die Abfrage einen zusätzlichen Parameter erwartet: 3 anstelle von 2.

Das Komische ist, dass die Abfrage , wenn ich während der Ausführung manuell containsPageableOrSortInQueryExpressionvon falsezu wechsle true, einwandfrei funktioniert, sodass ich nicht weiß, warum diese Zeichenfolge bei mir überprüft wird queryStringund ich nicht weiß, wie ich sie bereitstellen soll.

Jede Hilfe wäre sehr dankbar.

Update 30.01.2008 Es scheint, dass Entwickler des Spring-Data-Projekts mit einer PR von Jens Schauder an einer Lösung für dieses Problem arbeiten

Lasneyx
quelle
Nein, ich habe noch keine Lösung gefunden. Ich habe in Spring JIRA ein Ticket erstellt, aber es erfolgt keine Antwort: jira.spring.io/browse/DATAJPA-928 . Am Ende brauchte ich keine Paginierung, also habe ich nicht versucht, weiter nachzuforschen oder mit diesem Ticket härter zu pushen.
Lasneyx
3
Okay danke. Als Problemumgehung hätten Sie "\ n # pageable \ n" anstelle von "#pageable" hinzufügen können, aber ich hoffe, dass dies in Zukunft behoben wird.
Janar
1
Dieser aktuelle Fix machte die Sache für mich noch schlimmer. Jetzt wird mein PageAble-Parameter vollständig ignoriert und die Abfrage wird unbegrenzt ausgeführt. Wenn Sie also diese \ n # umsetzbare \ n Problemumgehung eingefügt haben, müssen Sie sie entfernen, da sonst Probleme auftreten.
Rüdiger Schulz

Antworten:

46

Ich entschuldige mich im Voraus, dies fasst die ursprüngliche Frage und den Kommentar von Janar ziemlich gut zusammen , aber ...

Ich habe das gleiche Problem: Ich fand das Beispiel 50 von Spring Data als Lösung für meine Notwendigkeit, eine native Abfrage mit Paginierung zu haben, aber Spring beschwerte sich beim Start, dass ich die Paginierung nicht mit nativen Abfragen verwenden konnte.

Ich wollte nur berichten, dass es mir gelungen ist, die benötigte native Abfrage mithilfe der Paginierung mit dem folgenden Code erfolgreich auszuführen:

    @Query(value="SELECT a.* "
            + "FROM author a left outer join mappable_natural_person p on a.id = p.provenance_id "
            + "WHERE p.update_time is null OR (p.provenance_name='biblio_db' and a.update_time>p.update_time)"
            + "ORDER BY a.id \n#pageable\n", 
        /*countQuery="SELECT count(a.*) "
            + "FROM author a left outer join mappable_natural_person p on a.id = p.provenance_id "
            + "WHERE p.update_time is null OR (p.provenance_name='biblio_db' and a.update_time>p.update_time) \n#pageable\n",*/
        nativeQuery=true)
public List<Author> findAuthorsUpdatedAndNew(Pageable pageable);

Die countQuery (die im Codeblock auskommentiert ist) wird Page<Author> als Rückgabetyp der Abfrage verwendet. Die Zeilenumbrüche um den Kommentar "#pageable" werden benötigt, um den Laufzeitfehler bei der Anzahl der erwarteten Parameter zu vermeiden (Problemumgehung des Problemumgehung). Ich hoffe, dieser Fehler wird bald behoben ...

Emanuele Fusco
quelle
1
In meinem Fall ?#{#pageable}funktioniert stattdessen \n#pageable\n.
Dmitry Stolbov
6
Das funktioniert. Für Argumente können Sie weiterhin benannte Parameter verwenden. Beispiel :authorId. Ich änderte Ihr \n#pageable\nzu --#pageable\nfür postgresql. Die countQuery wird wie von Ihnen vorgeschlagen benötigt, da in vielen Beispielen List <> anstelle von Page <> verwendet wird. Ihre Antwort war genau richtig. Ich habe die gleichen Dokumente durchgesehen und ohne Ihre Antwort hätte ich einfach aufgegeben.
Abhishek Dujari
2
Wenn Sie die countQuery nicht schreiben und das Framework dies handhaben lässt, wird die Anzahl der Abfragen manchmal nicht korrekt für Sie geschrieben, und möglicherweise wird ein Fehler als ungültige Feldliste angezeigt. Sie können die SQL-Anweisungen jederzeit analysieren, indem Sie der Eigenschaftendatei logging.level.org.hibernate.SQL = DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder = TRACE die folgenden Einträge hinzufügen Beheben Sie das Problem. Dies kann von den Framework-Versionen und der von Ihnen verwendeten Datenbank abhängen.
Nalaka
In meinem Fall /*:pageable*/funktioniert nur (mit SQL Server, Hibernate 5.4.1.Final und Spring Boot 2.2.1)
Marioosh
34

Dies ist ein Hack für Programme, die Spring Data JPA vor Version 2.0.4 verwenden .

Code hat mit PostgreSQL und MySQL funktioniert:

public interface UserRepository extends JpaRepository<User, Long> {

@Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1 ORDER BY ?#{#pageable}",
       countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
       nativeQuery = true)
   Page<User> findByLastname(String lastname, Pageable pageable);   
}

ORDER BY ?#{#pageable}ist für Pageable. countQueryist für Page<User>.

Dmitry Stolbov
quelle
Ich weiß nicht. Versuchen Sie 1. die Protokollierung von SQL-Anweisungen zu aktivieren - setzen Sie spring.jpa.show-sql=falseein application.properties; 2. ?#{#pageable}Legen Sie in Ihrer nativeQuery fest und 3. analysieren Sie das Ergebnis-SQL aus dem Protokoll. @Lasneyx hat die Ausgabe DATAJPA-928 für diese Besonderheit von Spring Data JPA erstellt.
Dmitry Stolbov
11

Nur für den Datensatz, bei dem H2 als Testdatenbank und MySQL zur Laufzeit verwendet werden, funktioniert dieser Ansatz (Beispiel ist das neueste Objekt in der Gruppe ):

@Query(value = "SELECT t.* FROM t LEFT JOIN t AS t_newer " +
        "ON t.object_id = t_newer.object_id AND t.id < t_newer.id AND o_newer.user_id IN (:user_ids) " +
        "WHERE t_newer.id IS NULL AND t.user_id IN (:user_ids) " +
        "ORDER BY t.id DESC \n-- #pageable\n",
        countQuery = "SELECT COUNT(1) FROM t WHERE t.user_id IN (:user_ids) GROUP BY t.object_id, t.user_id",
        nativeQuery = true)
Page<T> findByUserIdInGroupByObjectId(@Param("user_ids") Set<Integer> userIds, Pageable pageable);

Spring Data JPA 1.10.5, H2 1.4.194, MySQL Community Server 5.7.11-Protokoll (innodb_version 5.7.11).

maricn
quelle
Arbeitete für PostgreSQL. Vielen Dank!
Maksym Chernikov
Was ist, wenn wir die Reihenfolge von asc oder desc dynamisch an die Abfrage übergeben müssen?
Kulbhushan Singh
8

Ich habe genau das gleiche Symptom wie @Lasneyx. Meine Problemumgehung für die native Postgres-Abfrage

@Query(value = "select * from users where user_type in (:userTypes) and user_context='abc'--#pageable\n", nativeQuery = true)
List<User> getUsersByTypes(@Param("userTypes") List<String> userTypes, Pageable pageable);
689
quelle
Arbeitete auch für mich mit DB2.
Anders Metnik
@Query (nativeQuery = true, value = "wähle a. * Aus rqst a LEFT OUTER JOIN table_b b ON a.id = b.id wobei a.code = 'abc' ORDER BY - # pageable \ n") nicht funktioniert: Der Datentyp des Parameters $ 2
Developer
6

Versuche dies:

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1 ORDER BY /*#pageable*/",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

( "/* */"für Oracle notation)

XmasPiano
quelle
Aus irgendeinem Grund wurde eine Abfrage mit einem Komma nach dem Pageable erstellt. So etwas wie "ORDER BY / * # pageable * /" verursachte einen Syntaxfehler
d-man
Ich entferne die Bestellung durch aber generierte Ausnahme unerwartete
Zeichen
4

Ich benutze die Oracle-Datenbank und habe nicht das Ergebnis erhalten, sondern einen Fehler mit dem generierten Komma, über den D-Man oben gesprochen hat.

Dann war meine Lösung:

Pageable pageable = new PageRequest(current, rowCount);

Wie Sie ohne Reihenfolge beim Erstellen von Pagable sehen können.

Und die Methode im DAO:

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1 /*#pageable*/ ORDER BY LASTNAME",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
 }
Aufleveln
quelle
1

Beide folgenden Ansätze funktionieren gut mit MySQL zum Paginieren nativer Abfragen. Sie funktionieren jedoch nicht mit H2. Es wird den SQL-Syntaxfehler beschweren.

  • BESTELLEN NACH? # {# Pageable}
  • ORDER BY a.id \ n # pageable \ n
wmao
quelle
1

Die Verwendung von "ORDER BY id DESC \ n-- #pageable \ n" anstelle von "ORDER BY id \ n # pageable \ n" funktionierte bei mir mit MS SQL SERVER

Muhamed Saudi
quelle
1

Ich konnte Pagination erfolgreich integrieren

spring-data-jpa-2.1.6

wie folgt.

@Query(
 value = “SELECT * FROM Users”, 
 countQuery = “SELECT count(*) FROM Users”, 
 nativeQuery = true)
Page<User> findAllUsersWithPagination(Pageable pageable);
Udara Seneviratne
quelle
0

Es funktioniert wie folgt:

public interface UserRepository extends JpaRepository<User, Long> {
    @Query(value = "select * from (select (@rowid\\:=@rowid+1) as RN, u.* from USERS u, (SELECT @rowid\\:=0) as init where  LASTNAME = ?1) as total"+
        "where RN between ?#{#pageable.offset-1} and ?#{#pageable.offset + #pageable.pageSize}",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
    Page<User> findByLastname(String lastname, Pageable pageable);
}
Fonda Wu
quelle
0

Für mich arbeitete unten in MS SQL

 @Query(value="SELECT * FROM ABC r where r.type in :type  ORDER BY RAND() \n-- #pageable\n ",nativeQuery = true)
List<ABC> findByBinUseFAndRgtnType(@Param("type") List<Byte>type,Pageable pageable);
Amit Kumar
quelle
0

Ich verwende den folgenden Code. Arbeiten

@Query(value = "select * from user usr" +
  "left join apl apl on usr.user_id = apl.id" +
  "left join lang on lang.role_id = usr.role_id" +
  "where apl.scr_name like %:scrname% and apl.uname like %:uname and usr.role_id in :roleIds ORDER BY ?#{#pageable}",
  countQuery = "select count(*) from user usr" +
      "left join apl apl on usr.user_id = apl.id" +
      "left join lang on lang.role_id = usr.role_id" +
      "where apl.scr_name like %:scrname% and apl.uname like %:uname and usr.role_id in :roleIds",
  nativeQuery = true)
Page<AplUserEntity> searchUser(@Param("scrname") String scrname,@Param("uname") String  uname,@Param("roleIds") List<Long> roleIds,Pageable pageable);
Kübra
quelle
1
Können Sie bitte erklären, warum diese Antwort besser ist als alle anderen, die es schon seit mehreren Jahren gibt?
Dragon Thoughts
0

Das Entfernen von \ n # pageable \ n sowohl aus der Abfrage als auch aus der Zählabfrage hat bei mir funktioniert. Springboot-Version: 2.1.5.RELEASE DB: MySQL

Shubham Paldewar
quelle
0

Das hat bei mir (ich benutze Postgres) in Groovy funktioniert:

@RestResource(path="namespaceAndNameAndRawStateContainsMostRecentVersion", rel="namespaceAndNameAndRawStateContainsMostRecentVersion")
    @Query(nativeQuery=true,
            countQuery="""
            SELECT COUNT(1) 
            FROM 
            (
                SELECT
                ROW_NUMBER() OVER (
                    PARTITION BY name, provider_id, state
                    ORDER BY version DESC) version_partition,
                *
                FROM mydb.mytable
                WHERE
                (name ILIKE ('%' || :name || '%') OR (:name = '')) AND
                (namespace ILIKE ('%' || :namespace || '%') OR (:namespace = '')) AND
                (state = :state OR (:state = ''))
            ) t
            WHERE version_partition = 1
            """,
            value="""
            SELECT id, version, state, name, internal_name, namespace, provider_id, config, create_date, update_date 
            FROM 
            (
                SELECT 
                ROW_NUMBER() OVER (
                    PARTITION BY name, provider_id, state
                    ORDER BY version DESC) version_partition,
                *
                FROM mydb.mytable
                WHERE 
                (name ILIKE ('%' || :name || '%') OR (:name = '')) AND
                (namespace ILIKE ('%' || :namespace || '%') OR (:namespace = '')) AND
                (state = :state OR (:state = ''))       
            ) t            
            WHERE version_partition = 1             
            /*#{#pageable}*/
            """)
    public Page<Entity> findByNamespaceContainsAndNameContainsAndRawStateContainsMostRecentVersion(@Param("namespace")String namespace, @Param("name")String name, @Param("state")String state, Pageable pageable)

Der Schlüssel hier war zu verwenden: /*#{#pageable}*/

Es ermöglicht mir das Sortieren und Paginieren. Sie können es testen, indem Sie Folgendes verwenden: http: // localhost: 8080 / api / v1 / entity / search / namespaceAndNameAndRawStateContainsMostRecentVersion? Namespace = & name = & state = shared & page = 0 & size = 3 & sort = name, desc

Achten Sie auf dieses Problem: Spring Pageable übersetzt den @ Column-Namen nicht

Der Code-Typ
quelle
0

Sie können den folgenden Code für h2 und MySQl verwenden

    @Query(value = "SELECT req.CREATED_AT createdAt, req.CREATED_BY createdBy,req.APP_ID appId,req.NOTE_ID noteId,req.MODEL model FROM SUMBITED_REQUESTS  req inner join NOTE note where req.NOTE_ID=note.ID and note.CREATED_BY= :userId "
        ,
         countQuery = "SELECT count(*) FROM SUMBITED_REQUESTS req inner join NOTE note WHERE req.NOTE_ID=note.ID and note.CREATED_BY=:userId",
        nativeQuery = true)
Page<UserRequestsDataMapper> getAllRequestForCreator(@Param("userId") String userId,Pageable pageable);
Salah Atwa
quelle
-1

Durch Ersetzen von / #pageable / durch? # {# Pageable} kann die Paginierung durchgeführt werden. Durch Hinzufügen von PageableDefault können Sie die Größe der Seitenelemente festlegen.

Mohit Thakkar
quelle