setMaxResults für Spring-Data-JPA-Annotation?

127

Ich versuche, Spring-Data-JPA in mein Projekt einzubeziehen. Eine Sache, die mich verwirrt, ist, wie ich setMaxResults (n) durch Annotation erreiche.

Zum Beispiel mein Code:

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

Ich muss nur one (and only one)User von otherObj zurückgeben, kann aber keine Möglichkeit finden, die maxResults mit Anmerkungen zu versehen. Kann mir jemand einen Hinweis geben?

(MySQL beschwert sich:

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

)

Ich habe einen Link gefunden: https://jira.springsource.org/browse/DATAJPA-147 . Ich habe es versucht, bin aber gescheitert. Es scheint jetzt nicht möglich? Warum ist eine so wichtige Funktion nicht in Spring-Data integriert?

Wenn ich diese Funktion manuell implementiere:

public class UserRepositoryImpl implements UserRepository

Ich muss Tonnen von vordefinierten Methoden implementieren CrudRepository, das wäre schrecklich.

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

smallufo
quelle

Antworten:

175

Ab Spring Data JPA 1.7.0 (Evans Release Train).

Sie können die neu eingeführten Schlüsselwörter Topund FirstSchlüsselwörter verwenden, mit denen Sie Abfragemethoden wie folgt definieren können:

findTop10ByLastnameOrderByFirstnameAsc(String lastname);

Spring Data begrenzt die Ergebnisse automatisch auf die von Ihnen definierte Anzahl (standardmäßig 1, wenn sie weggelassen wird). Beachten Sie, dass die Reihenfolge der Ergebnisse hier relevant wird (entweder durch eine OrderByim Beispiel gezeigte Klausel oder durch Übergabe eines SortParameters an die Methode). Weitere Informationen hierzu finden Sie im Blogbeitrag über die neuen Funktionen des Spring Data Evans-Release-Zugs oder in der Dokumentation .

Für frühere Versionen

Um nur Datenscheiben abzurufen, verwendet Spring Data die Paginierungsabstraktion, die Pageableauf der anfordernden Seite eine Schnittstelle sowie Pageauf der Ergebnisseite eine Abstraktion enthält. Sie könnten also damit beginnen

public interface UserRepository extends Repository<User, Long> {

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

und benutze es so:

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

Wenn Sie den Kontext des Ergebnisses kennen müssen (welche Seite ist es tatsächlich? Ist es die erste? Wie viele gibt es insgesamt?), Verwenden Sie Pageals Rückgabetyp:

public interface UserRepository extends Repository<User, Long> {

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

Der Client-Code kann dann Folgendes tun:

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

Nicht, dass wir eine Zählprojektion der tatsächlich Pageauszuführenden Abfrage auslösen würden, falls Sie als Rückgabetyp verwenden, da wir herausfinden müssen, wie viele Elemente insgesamt zur Berechnung der Metadaten vorhanden sind. Stellen Sie darüber hinaus sicher, dass Sie die PageRequestInformationen tatsächlich mit Sortierinformationen ausstatten , um stabile Ergebnisse zu erzielen. Andernfalls können Sie die Abfrage zweimal auslösen und unterschiedliche Ergebnisse erhalten, auch ohne dass sich die Daten darunter geändert haben.

Oliver Drotbohm
quelle
14
Vielen Dank, aber ich hoffe immer noch auf eine auf Anmerkungen basierende Lösung. Weil 'Page / Pageable' in diesem Fall zu übertrieben sind. Und der Kunde muss eine Pageable / Page erstellen, um "ein und nur ein" Ergebnis abzurufen ... Die Verwendung ist sehr unfreundlich. Und anscheinend sind Sie für Spring-Data verantwortlich. Ich hoffe, Sie können dieses Problem weiter untersuchen: stackoverflow.com/questions/9276461/… . Können Sie bestätigen, ob es sich um den Fehler von Spring-Data handelt?
Smallufo
1
+ das funktioniert, danke, aber eine auf Anmerkungen basierende Lösung wäre in neueren Versionen besser
azerafati
1
Hier wird eine Zählabfrage ausgelöst, die völlig unnötig ist, wenn wir eine begrenzte Abfrage wünschen.
Azerafati
1
Dies ist nicht der Fall, wenn Sie Listden Rückgabetyp der Methode verwenden.
Oliver Drotbohm
3
Ich denke, Topund FirstSchlüsselwörter sind nicht auf Methoden anwendbar, die @QueryAnmerkungen verwenden? Nur diejenigen, die eine auf Methodennamen basierende Abfragegenerierung verwenden?
Ruslan Stelmachenko
106

Wenn Sie Java 8 und Spring Data 1.7.0 verwenden, können Sie Standardmethoden verwenden, wenn Sie eine @QueryAnmerkung mit dem Festlegen maximaler Ergebnisse kombinieren möchten :

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
quelle
3
potenziell nützlich für ein einzelnes ErgebnisStream<User> ... {...} default Optional<User> ... { ...findAny() }
Xenoterracide
28

Es gibt eine Möglichkeit, das Äquivalent von "a setMaxResults (n) durch Annotation" wie folgt bereitzustellen:

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

Dies sollte den Trick tun, wenn ein Pageable als Parameter gesendet wird. Um beispielsweise die ersten 10 Datensätze abzurufen, müssen Sie pageable auf diesen Wert setzen:

new PageRequest(0, 10)
ncaralicea
quelle
Wenn Sie Pagable verwenden, dann ist seine Rückgabeseite <XYZ> nicht Liste <XYZ>
Arundev
@Arundev falsch - Listfunktioniert perfekt (und vermeidet eine unnötige countAbfrage, wie bereits in einem vorherigen Kommentar erwähnt )
Janaka Bandara
21

Verwenden Sie Spring Data Evans (1.7.0 RELEASE)

Die neue Version von Spring Data JPA mit einer weiteren Liste von Modulen namens Evans bietet die Funktion, Schlüsselwörter Top20und zu verwendenFirst das Abfrageergebnis einzuschränken.

so könntest du jetzt schreiben

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

oder

List<User> findTop20ByLastnameOrderByIdDesc(String lastname);

oder für ein einzelnes Ergebnis

List<User> findFirstByLastnameOrderByIdDesc(String lastname);
Azerafati
quelle
5
Optional <Benutzer> findFirstByLastnameOrderByIdDesc (String lastname);
krmanish007
Wie würde ich nach Erhalt der Top20-Ergebnisse die nächsten 20 Ergebnisse erhalten, wenn ich mit Paginierung zur nächsten Seite der Website gehe?
Kittu
@kittu, das ist natürlich eine andere Frage, aber du musst ein PageableObjekt übergeben.
Schauen
Warum haben sie die Methode dazu gebracht, eine verdammte Liste zurückzugeben, wenn sie ZUERST angegeben ist?
Vasile Surdu
13

Die beste Wahl für mich ist die native Abfrage:

@Query(value="SELECT * FROM users WHERE other_obj = ?1 LIMIT 1", nativeQuery = true)
User findByOhterObj(OtherObj otherObj);
Grigory Kislin
quelle
Nicht sicher, ob das Limit in Hibernate unterstützt wird: forum.hibernate.org/viewtopic.php?f=9&t=939314
Witold Kaczurba
2
Bricht das ganze Konzept! Verwenden Sie stattdessen lieber EntityManager!
Spektakulatius
@ Code.IT Ich propagiere das KISS-Konzept. Abgesehen davon wurde der JPA- nativeQuery Annotationsparameter hinzugefügt, der nicht ignoriert werden soll.
Grigory Kislin
@GKislin als Sie Ihre Methode wie findLimitOneByOtherObj umbenennen müssen. Außerdem haben Sie vergessen, ein OrderBy hinzuzufügen, was zu einem falschen Ergebnis führt, wenn das other_obj nicht eindeutig ist. Andernfalls sollte eine where-Anweisung für eine eindeutige Spalte ohne Einschränkung ausreichen
Spektakulatius
LIMIT Schlüsselwort ist nicht Ruhezustand, sondern postgres / mysql
Acewin
5

Wenn Ihre Klasse @Repositoryerweitert wird JpaRepository, können Sie das folgende Beispiel verwenden.

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

getContent return a List<Transaction>.

Luis Camargo
quelle
Was ist, wenn ich 2 wo Bedingungen in der Abfrage habe, wie transactionRepository.findByFirstNameAndLastName (Vorname, Nachname)? wird es funktionieren ? Ich erhalte diese Ausnahme org.springframework.data.mapping.PropertyReferenceException: Für den Typ Board wurde keine Eigenschaft erstellt.
Shailesh
4

Es ist auch möglich, @QueryHints zu verwenden. Das folgende Beispiel verwendet 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();
Krake
quelle
Ich habe diesen Code getestet, aber er funktioniert nicht. Keine Ausnahme, aber kein richtiger Ergebnisnachbarn. Auch die Dokumentation zu QueryHints ist sehr dünn. Siehe unten. docs.spring.io/spring-data/jpa/docs/1.7.1.RELEASE/reference/…
bogdan.rusu
IIRC gibt es keine Garantie dafür, dass ein Abfragehinweis befolgt wird. Es ist nur eine Möglichkeit, "freundliche Empfehlungen" an die Implementierung weiterzugeben.
Priidu Neemre
Ich versuche es mit @QueryHints({@QueryHint(name = org.hibernate.annotations.FETCH_SIZE, value = "1")})in Hibernate, aber Alias ​​:( funktioniert nicht
Grigory Kislin
2
org.hibernate.annotations.FETCH_SIZE seine Abrufgröße, seine nicht begrenzte, nicht maximale Ergebnisse. Hiber kann nicht
Zhuravskiy Vitaliy
4

new PageRequest(0,10)funktioniert nicht in neueren Spring-Versionen (ich benutze 2.2.1.RELEASE). Grundsätzlich hat der Konstruktor einen zusätzlichen Parameter als SortTyp erhalten. Darüber hinaus müssen Sie für den Konstruktor protectedentweder eine seiner untergeordneten Klassen verwenden oder seine ofstatische Methode aufrufen :

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

Sie können auch die Verwendung von SortParametern weglassen und implizit die Standardsortierung (Sortierung nach pk usw.) verwenden:

PageRequest.of(0, 10)

Ihre Funktionsdeklaration sollte ungefähr so ​​aussehen:

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

und die Funktion wird sein:

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