setMaxResults pour l'annotation Spring-Data-JPA?

128

J'essaye d'intégrer Spring-Data-JPA dans mon projet. Une chose qui me trouble est de savoir comment obtenir setMaxResults (n) par annotation?

par exemple, mon code:

public interface UserRepository extends CrudRepository<User , Long>
{
  @Query(value="From User u where u.otherObj = ?1 ")
  public User findByOhterObj(OtherObj otherObj);
}

Je n'ai besoin que de renvoyer one (and only one)User depuis otherObj, mais je ne trouve pas de moyen d'annoter les maxResults. Quelqu'un peut-il me donner un indice?

(mysql se plaint:

com.mysql.jdbc.JDBC4PreparedStatement@5add5415: select user0_.id as id100_, user0_.created as created100_ from User user0_ where user0_.id=2 limit ** NOT SPECIFIED **
WARN  util.JDBCExceptionReporter - SQL Error: 0, SQLState: 07001
ERROR util.JDBCExceptionReporter - No value specified for parameter 2

)

J'ai trouvé un lien: https://jira.springsource.org/browse/DATAJPA-147 , j'ai essayé mais j'ai échoué. Cela ne semble pas possible maintenant? Pourquoi une fonctionnalité aussi importante n'est-elle pas intégrée à Spring-Data?

Si j'implémente cette fonctionnalité manuellement:

public class UserRepositoryImpl implements UserRepository

Je dois implémenter des tonnes de méthodes prédéfinies dans CrudRepository, ce serait terrible.

environnements: spring-3.1, spring-data-jpa-1.0.3.RELEASE.jar, spring-data-commons-core-1.1.0.RELEASE.jar

smallufo
la source

Réponses:

176

À partir de Spring Data JPA 1.7.0 (Evans Release Train).

Vous pouvez utiliser les nouveaux mots clés Topet Firstqui vous permettent de définir des méthodes de requête comme celle-ci:

findTop10ByLastnameOrderByFirstnameAsc(String lastname);

Spring Data limitera automatiquement les résultats au nombre que vous avez défini (par défaut à 1 si omis). Notez que l'ordre des résultats devient ici pertinent (soit via une OrderByclause comme vu dans l'exemple, soit en transmettant un Sortparamètre à la méthode). En savoir plus à ce sujet dans l'article de blog couvrant les nouvelles fonctionnalités du train de versions Spring Data Evans ou dans la documentation .

Pour les versions précédentes

Pour récupérer uniquement des tranches de données, Spring Data utilise l'abstraction de pagination qui est fournie avec une Pageableinterface du côté demandeur ainsi qu'une Pageabstraction du côté résultat des choses. Donc tu pourrais commencer par

public interface UserRepository extends Repository<User, Long> {

  List<User> findByUsername(String username, Pageable pageable);
}

et utilisez-le comme ceci:

Pageable topTen = new PageRequest(0, 10);
List<User> result = repository.findByUsername("Matthews", topTen);

Si vous avez besoin de connaître le contexte du résultat (de quelle page s'agit-il réellement? Est-ce la première? Combien y en a-t-il au total?), Utilisez Pagecomme type de retour:

public interface UserRepository extends Repository<User, Long> {

  Page<User> findByUsername(String username, Pageable pageable);
}

Le code client peut alors faire quelque chose comme ceci:

Pageable topTen = new PageRequest(0, 10);
Page<User> result = repository.findByUsername("Matthews", topTen);
Assert.assertThat(result.isFirstPage(), is(true));

Non pas que nous déclencherons une projection de comptage de la requête réelle à exécuter au cas où vous l'utiliseriez Pagecomme type de retour, car nous avons besoin de savoir combien d'éléments il y a au total pour calculer les métadonnées. Au-delà de cela, assurez-vous d'équiper le PageRequestavec des informations de tri pour obtenir des résultats stables. Sinon, vous pourriez déclencher la requête deux fois et obtenir des résultats différents même sans que les données aient changé en dessous.

Oliver Drotbohm
la source
14
Merci, mais j'espère toujours une solution «basée sur les annotations». Parce que «Page / Pageable» est trop exagéré dans ce cas. Et le client doit créer un Pageable / Page pour récupérer «un et un seul» résultat ... C'est très peu convivial à utiliser. Et il semble que vous soyez en charge de Spring-Data, j'espère que vous pourrez approfondir ce problème: stackoverflow.com/questions/9276461/… . Pouvez-vous confirmer s'il s'agit du bogue de Spring-Data?
smallufo
1
+ cela fonctionne, merci mais une solution basée sur les annotations serait meilleure dans les versions plus récentes
azerafati
1
une requête de comptage est déclenchée ici, ce qui est totalement inutile si nous voulons une requête limitée.
azerafati
1
Ce n'est pas le cas si vous utilisez Listcomme type de retour de la méthode.
Oliver Drotbohm
3
Je pense, Topet les Firstmots-clés ne sont pas applicables aux méthodes qui utilisent l' @Queryannotation? Seuls ceux qui utilisent la génération de requêtes basée sur le nom de la méthode?
Ruslan Stelmachenko
106

Si vous utilisez Java 8 et Spring Data 1.7.0, vous pouvez utiliser les méthodes par défaut si vous souhaitez combiner une @Queryannotation avec la définition de résultats maximum:

public interface UserRepository extends PagingAndSortingRepository<User,Long> {
  @Query("from User u where ...")
  List<User> findAllUsersWhereFoo(@Param("foo") Foo foo, Pageable pageable);

  default List<User> findTop10UsersWhereFoo(Foo foo) {
    return findAllUsersWhereFoo(foo, new PageRequest(0,10));
  }

}
Wim Deblauwe
la source
3
potentiellement utile pour un seul résultatStream<User> ... {...} default Optional<User> ... { ...findAny() }
xenoterracide
28

Il existe un moyen de fournir l'équivalent de "un setMaxResults (n) par annotation" comme dans l'exemple suivant:

public interface ISomething extends JpaRepository<XYZ, Long>
{
    @Query("FROM XYZ a WHERE a.eventDateTime < :before ORDER BY a.eventDateTime DESC")
    List<XYZ> findXYZRecords(@Param("before") Date before, Pageable pageable);
}

Cela devrait faire l'affaire, lorsqu'un pageable est envoyé comme paramètre. Par exemple, pour récupérer les 10 premiers enregistrements, vous devez définir paginable sur cette valeur:

new PageRequest(0, 10)
ncaralicea
la source
Si vous utilisez Pagable, sa page de retour <XYZ> pas la liste <XYZ>
Arundev
@Arundev faux - Listfonctionne parfaitement (et évite une countrequête inutile comme déjà mentionné dans un commentaire précédent )
Janaka Bandara
21

Utiliser Spring Data Evans (1.7.0 RELEASE)

la nouvelle version de Spring Data JPA avec une autre liste de modules ensemble appelée Evans a la particularité d'utiliser des mots Top20- clés et Firstde limiter le résultat de la requête,

pour que tu puisses maintenant écrire

List<User> findTop20ByLastname(String lastname, Sort sort);

ou

List<User> findTop20ByLastnameOrderByIdDesc(String lastname);

ou pour un seul résultat

List<User> findFirstByLastnameOrderByIdDesc(String lastname);
azerafati
la source
5
Facultatif <User> findFirstByLastnameOrderByIdDesc (String lastname);
krmanish007
Après avoir obtenu les 20 meilleurs résultats, comment pourrais-je obtenir les 20 résultats suivants lorsque je vais à la page suivante du site Web en utilisant la pagination?
kittu
@kittu, c'est bien sûr une autre question mais vous devez passer un Pageableobjet. consultez la documentation du printemps
azerafati
pourquoi ils ont fait que la méthode renvoie une fichue liste si elle est spécifiée en PREMIER ??
Vasile Surdu
13

Le meilleur choix pour moi est la requête native:

@Query(value="SELECT * FROM users WHERE other_obj = ?1 LIMIT 1", nativeQuery = true)
User findByOhterObj(OtherObj otherObj);
Grigory Kislin
la source
Je ne sais pas si la limite est prise en charge dans Hibernate: forum.hibernate.org/viewtopic.php?f=9&t=939314
Witold Kaczurba
2
Brise tout le concept! Mieux vaut utiliser EntityManager à la place!
Spektakulatius
@ Code.IT Je propage le concept KISS. Mis à part ce nativeQuery paramètre d'annotation JPA ajouté non pour ignorer.
Grigory Kislin du
@GKislin que vous devez renommer votre méthode comme findLimitOneByOtherObj. Vous avez également oublié d'ajouter un OrderBy qui conduit à un résultat erroné si l'autre_obj n'est pas unique. Sinon, une instruction where sur une colonne unique devrait suffire sans une limite
Spektakulatius
Le mot clé LIMIT n'est pas d'hibernation mais postgres / mysql
Acewin
5

Si votre classe @Repositorys'étend, JpaRepositoryvous pouvez utiliser l'exemple ci-dessous.

int limited = 100;
Pageable pageable = new PageRequest(0,limited);
Page<Transaction> transactionsPage = transactionRepository.findAll(specification, pageable);
return transactionsPage.getContent();

getContent renvoie un fichier List<Transaction>.

Luis Camargo
la source
que se passe-t-il si j'ai 2 conditions where dans la requête, comme transactionRepository.findByFirstNameAndLastName (prénom, nom)? est-ce que ça marchera ? J'obtiens cette exception org.springframework.data.mapping.PropertyReferenceException: Aucune propriété créée n'a été trouvée pour le type Board.
Shailesh
4

C'est également possible en utilisant @QueryHints. L'exemple ci-dessous utilise org.eclipse.persistence.config.QueryHints # JDBC_MAX_ROWS

@Query("SELECT u FROM User u WHERE .....")
@QueryHints(@QueryHint(name = JDBC_MAX_ROWS, value = "1"))
Voter findUser();
kraken
la source
J'ai testé ce code mais cela ne fonctionne pas. Pas d'exception, mais pas de résultat plus proche. De plus, la documentation est très mince sur QueryHints. Voir ci-dessous. docs.spring.io/spring-data/jpa/docs/1.7.1.RELEASE/reference/…
bogdan.rusu
IIRC, il n'y a aucune garantie qu'un indice de requête sera suivi. C'est juste un moyen de transmettre des «recommandations amicales» à l'implémentation.
Priidu Neemre
Je l'essaie avec @QueryHints({@QueryHint(name = org.hibernate.annotations.FETCH_SIZE, value = "1")})dans Hibernate, mais alias: (ne fonctionne pas
Grigory Kislin
2
org.hibernate.annotations.FETCH_SIZE sa taille de récupération, ce n'est pas une limite, pas des résultats maximum. Hiber cant
Zhuravskiy Vitaliy
4

new PageRequest(0,10)ne fonctionne pas dans les versions plus récentes de Spring (j'utilise 2.2.1.RELEASE). Fondamentalement, le constructeur a un paramètre supplémentaire comme Sorttype. De plus, le constructeur est protecteddonc obligé d'utiliser l'une de ses classes enfants ou d'appeler sa ofméthode statique:

PageRequest.of(0, 10, Sort.sort(User.class).by(User::getFirstName).ascending()))

Vous pouvez également omettre l'utilisation du Sortparamètre et utiliser implicitement le tri par défaut (tri par pk, etc.):

PageRequest.of(0, 10)

Votre déclaration de fonction devrait ressembler à ceci:

List<User> findByUsername(String username, Pageable pageable)

et la fonction sera:

userRepository.findByUsername("Abbas", PageRequest.of(0,10, Sort.sort(User.class).by(User::getLastName).ascending());
Abbas Hosseini
la source