web-dev-qa-db-fra.com

Données de printemps et requête native avec pagination

Dans un projet Web, en utilisant la dernière version de spring-data (1.10.2) avec une base de données MySQL 5.6, j'essaie d'utiliser une requête native avec pagination, mais je rencontre un org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException au démarrage. 

UPDATE: 20180306 Ce problème est maintenant résolu dans Spring 2.0.4 Pour ceux qui sont toujours intéressés ou coincés dans des versions plus anciennes, vérifiez les réponses et les commentaires associés pour y remédier.

Selon Exemple 50 dans Utilisation de @Query à partir de la documentation de spring-data , il est possible de spécifier la requête elle-même et un countQuery, comme ceci:

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

Par curiosité, dans la classe NativeJpaQuery, je vois qu’elle contient le code suivant pour vérifier s’il s’agit d’une requête jpa valide:

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

Ma requête contient un paramètre Pageable, donc hasPagingOrSortingParameter est true, mais elle recherche également une séquence #pageable ou #sort à l'intérieur de la queryString, ce que je ne fournis pas.

J'ai essayé d'ajouter #pageable (c'est un commentaire) à la fin de ma requête, ce qui permet de valider la validation, mais échoue à l'exécution en indiquant que la requête attend un paramètre supplémentaire: 3 au lieu de 2.

Ce qui est drôle, c’est que, si je change manuellement containsPageableOrSortInQueryExpression de false en true en cours d’exécution, la requête fonctionne correctement; je ne sais donc pas pourquoi il vérifie que cette chaîne est à ma queryString et je ne sais pas comment la fournir.

Toute aide serait très appréciée.

Mise à jour 30/01/2018.__ Il semble que les développeurs de Spring-Data Project travaillent sur un correctif pour ce problème avec un PR de Jens Schauder

38
Lasneyx

Toutes mes excuses par avance, je résume assez bien la question initiale et le commentaire de Janar , cependant ...

Je rencontre le même problème: j'ai trouvé le Exemple 50 de Spring Data comme solution à mon besoin d'avoir une requête native avec pagination, mais Spring se plaignait au démarrage que je ne pouvais pas utiliser la pagination avec des requêtes natives.

Je voulais simplement signaler que j'avais réussi à exécuter la requête native dont j'avais besoin, à l'aide de la pagination, avec le code suivant:

    @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);

La méthode countQuery (commentée dans le bloc de code) est nécessaire pour utiliser Page<Author> En tant que type de retour de la requête, les nouvelles lignes autour du commentaire "#pageable" sont nécessaires pour éviter l'erreur d'exécution sur le nombre de paramètres attendus. (solution de contournement de la solution de contournement). J'espère que ce bug sera bientôt corrigé ...

31
Emanuele Fusco

Ce code a fonctionné avec PostgreSQL et MySQL:

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} est pour Pageable.countQuery est pour Page<>.

15
Dmitry Stolbov

Pour mémoire, en utilisant H2 comme base de test et MySQL au moment de l'exécution, cette approche fonctionne (l'exemple est le dernier objet du groupe ):

@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-log (innodb_version 5.7.11).

10
maricn

J'ai exactement le même symptôme que @Lasneyx. Ma solution de contournement pour la requête native Postgres

@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);
6
689

Essaye ça:

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

("/* */" pour Oracle notation)

5
XmasPiano

J'utilise la base de données Oracle et je n'ai pas obtenu le résultat mais une erreur avec la virgule générée dont d-man parle ci-dessus.

Alors ma solution était:

Pageable pageable = new PageRequest(current, rowCount);

Comme vous pouvez le voir sans ordre, créez quand Pagable.

Et la méthode dans le 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);
 }
3
Pulszar

Les deux approches suivantes fonctionnent bien avec MySQL pour la pagination de requêtes natives. Ils ne fonctionnent pas avec H2 cependant. Il se plaindra de l'erreur de syntaxe SQL.

  • ORDER BY? # {# Pageable}
  • ORDER BY a.id # pageable\n
2
wmao

Utiliser "ORDER BY id \\" #pageable\n " Au lieu de" ORDER BY id\n # pageable\n "a fonctionné pour moi avec MS SQL SERVER

1
Muhamed Saudi

Remplacement de / #pageable / avec? # {# Pageable} permet de faire une pagination . L'ajout de PageableDefault vous permet de définir la taille des éléments de la page. 

0
Mohit Thakkar

Cela fonctionne comme ci-dessous:

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);
}
0
Fonda Wu