setMaxResults dla adnotacji Spring-Data-JPA?

128

Próbuję włączyć Spring-Data-JPA do mojego projektu. Jedyną rzeczą, która mnie wprawia w zakłopotanie, jest to, w jaki sposób mogę osiągnąć setMaxResults (n) za pomocą adnotacji?

na przykład mój kod:

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

Muszę tylko zwrócić one (and only one)użytkownika z otherObj, ale nie mogę znaleźć sposobu na dodanie adnotacji do maxResults. Czy ktoś może mi podpowiedzieć?

(mysql narzeka:

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

)

Znalazłem link: https://jira.springsource.org/browse/DATAJPA-147 , próbowałem, ale nie udało się. Teraz wydaje się to niemożliwe? Dlaczego tak ważna funkcja nie jest wbudowana w Spring-Data?

Jeśli zaimplementuję tę funkcję ręcznie:

public class UserRepositoryImpl implements UserRepository

Muszę zaimplementować mnóstwo predefiniowanych metod CrudRepository, to byłoby straszne.

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

smallufo
źródło

Odpowiedzi:

176

Od Spring Data JPA 1.7.0 (wersja Evansa).

Możesz użyć nowo wprowadzonych Topi Firstsłów kluczowych, które pozwalają zdefiniować metody zapytań w następujący sposób:

findTop10ByLastnameOrderByFirstnameAsc(String lastname);

Spring Data automatycznie ograniczy wyniki do zdefiniowanej liczby (domyślnie 1, jeśli zostanie pominięty). Zauważ, że kolejność wyników staje się istotna w tym przypadku (poprzez OrderByklauzulę, jak widać w przykładzie, lub przez przekazanie Sortparametru do metody). Przeczytaj więcej na ten temat w poście na blogu dotyczącym nowych funkcji pociągu wydania Spring Data Evans lub w dokumentacji .

W przypadku poprzednich wersji

Aby pobrać tylko wycinki danych, Spring Data używa abstrakcji stronicowania, która jest dostarczana z Pageableinterfejsem po stronie żądającej, a także Pageabstrakcji po stronie wyników. Więc możesz zacząć

public interface UserRepository extends Repository<User, Long> {

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

i użyj go w ten sposób:

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

Jeśli chcesz poznać kontekst wyniku (która to właściwie strona? Czy to pierwsza? Ile ich jest w sumie?), Użyj Pagejako zwracanego typu:

public interface UserRepository extends Repository<User, Long> {

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

Kod klienta może wtedy zrobić coś takiego:

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

Nie oznacza to, że wyzwolimy projekcję liczby rzeczywistego zapytania do wykonania w przypadku, gdy użyjesz Pagejako typu zwracanego, ponieważ musimy dowiedzieć się, ile elementów jest w sumie do obliczenia metadanych. Poza tym upewnij się, że faktycznie wyposażasz urządzenie PageRequestw sortowanie informacji, aby uzyskać stabilne wyniki. W przeciwnym razie możesz wywołać zapytanie dwukrotnie i uzyskać różne wyniki, nawet bez zmiany danych poniżej.

Oliver Drotbohm
źródło
14
Dzięki, ale nadal mam nadzieję na rozwiązanie oparte na adnotacjach. Ponieważ „Page / Pageable” to w tym przypadku zbyt wiele. A klient musi stworzyć Pageable / Pageable, aby pobrać „jeden i tylko jeden” wynik ... Jest to bardzo nieprzyjazne w użyciu. Wygląda na to, że odpowiadasz za Spring-Data. Mam nadzieję, że możesz dokładniej przyjrzeć się temu problemowi: stackoverflow.com/questions/9276461/… . Czy możesz potwierdzić, czy to błąd Spring-Data?
smallufo
1
+ to działa, dzięki, ale rozwiązanie oparte na adnotacjach byłoby lepsze w nowszych wersjach
azerafati
1
wywoływane jest tutaj zapytanie zliczające, które jest całkowicie niepotrzebne, jeśli chcemy ograniczyć zapytanie.
azerafati
1
Nie dzieje się tak, jeśli używasz Listjako zwracanego typu metody.
Oliver Drotbohm
3
Myślę, Topa Firstsłowa kluczowe nie mają zastosowania do metod, które używają @Queryadnotacji? Tylko te, które używają generowania zapytań na podstawie nazwy metody?
Ruslan Stelmachenko
106

Jeśli używasz języka Java 8 i Spring Data 1.7.0, możesz użyć metod domyślnych, jeśli chcesz połączyć @Queryadnotację z ustawieniem maksymalnych wyników:

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
źródło
3
potencjalnie przydatne dla pojedynczego wynikuStream<User> ... {...} default Optional<User> ... { ...findAny() }
xenoterracide
28

Istnieje sposób na zapewnienie odpowiednika „a setMaxResults (n) przez adnotację”, jak poniżej:

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

Powinno to załatwić sprawę, gdy jako parametr wysyłany jest stronicowany element. Na przykład, aby pobrać pierwsze 10 rekordów, musisz ustawić stronicowanie na tę wartość:

new PageRequest(0, 10)
ncaralicea
źródło
Jeśli używasz Pagable, to jego powrót Strona <XYZ> a nie Lista <XYZ>
Arundev
@Arundev źle - Listdziała doskonale (i pozwala uniknąć niepotrzebnego countzapytania, jak już wspomniano w poprzednim komentarzu )
Janaka Bandara
21

Użyj Spring Data Evans (1.7.0 RELEASE)

nowa wersja Spring Data JPA z kolejną listą modułów o nazwie Evans ma funkcję używania słów kluczowych Top20iFirst ograniczania wyniku zapytania,

więc możesz teraz pisać

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

lub

List<User> findTop20ByLastnameOrderByIdDesc(String lastname);

lub dla pojedynczego wyniku

List<User> findFirstByLastnameOrderByIdDesc(String lastname);
azerafati
źródło
5
Opcjonalne <User> findFirstByLastnameOrderByIdDesc (String lastname);
krmanish007
Po uzyskaniu 20 najlepszych wyników, w jaki sposób mogę uzyskać następnych 20 wyników, kiedy przejdę do następnej strony w witrynie przy użyciu paginacji?
kittu
@kittu, to oczywiście inne pytanie, ale musisz przekazać Pageableobiekt. sprawdź dokumentację wiosenną
azerafati
dlaczego sprawili, że metoda zwraca cholerną Listę, jeśli jest określona PIERWSZA?
Vasile Surdu
13

Dla mnie najlepszym wyborem jest zapytanie natywne:

@Query(value="SELECT * FROM users WHERE other_obj = ?1 LIMIT 1", nativeQuery = true)
User findByOhterObj(OtherObj otherObj);
Grigorij Kislin
źródło
Nie jestem pewien, czy limit jest obsługiwany w Hibernate: forum.hibernate.org/viewtopic.php?f=9&t=939314
Witold Kaczurba
2
Łamie całą koncepcję! Zamiast tego lepiej użyj EntityManager!
Spektakulatius
@ Code.IT Propaguję koncepcję KISS. Poza tym nativeQuery dodany parametr adnotacji JPA nie do ignorowania.
Grigorij Kislin,
@GKislin, niż musisz zmienić nazwę metody, na przykład findLimitOneByOtherObj. Zapomniałeś również dodać OrderBy, co prowadzi do niewłaściwego wyniku, jeśli other_obj nie jest unikalne. W przeciwnym razie stwierdzenie gdzie na unikalnej kolumnie powinno wystarczyć bez ograniczenia
Spektakulatius
Słowo kluczowe LIMIT nie jest hibernacją, ale postgres / mysql
Acewin
5

Jeśli Twoja klasa się @Repositoryrozszerza JpaRepository, możesz skorzystać z poniższego przykładu.

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

getContent zwraca plik List<Transaction>.

Luis Camargo
źródło
co jeśli mam 2 warunki w zapytaniu, takie jak transactionRepository.findByFirstNameAndLastName (imię, nazwisko)? czy to zadziała ? Otrzymuję ten wyjątek org.springframework.data.mapping.PropertyReferenceException: Nie znaleziono tworzenia właściwości dla typu Board.
Shailesh
4

Jest to również możliwe przy użyciu @QueryHints. Poniższy przykład używa 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
źródło
Przetestowałem ten kod, ale nie działa. Bez wyjątku, ale bez właściwego wyniku. Również dokumentacja dotycząca QueryHints jest bardzo cienka. Zobacz poniżej. docs.spring.io/spring-data/jpa/docs/1.7.1.RELEASE/reference/…
bogdan.rusu
IIRC, nie ma gwarancji, że zostanie zastosowana wskazówka dotycząca zapytania. To tylko sposób na przekazanie „przyjaznych rekomendacji” do wdrożenia.
Priidu Neemre
Próbuję @QueryHints({@QueryHint(name = org.hibernate.annotations.FETCH_SIZE, value = "1")})w Hibernate, ale alias :( nie działa
Grigorij Kislin
2
org.hibernate.annotations.FETCH_SIZE to rozmiar pobierania, nie jest to limit, a nie maksymalna liczba wyników. Hiber cant
Zhuravskiy Vitaliy
4

new PageRequest(0,10)nie działa w nowszych wersjach Springa (używam 2.2.1.RELEASE). Zasadniczo konstruktor otrzymał dodatkowy parametr jako Sorttyp. Ponadto konstruktor jest protectedtaki, że musisz albo użyć jednej z jego klas potomnych, albo wywołać ofmetodę statyczną:

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

Możesz również pominąć użycie Sortparametru i niejawnie użyć domyślnego sortowania (sortowanie według pk itp.):

PageRequest.of(0, 10)

Twoja deklaracja funkcji powinna wyglądać mniej więcej tak:

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

a funkcja będzie:

userRepository.findByUsername("Abbas", PageRequest.of(0,10, Sort.sort(User.class).by(User::getLastName).ascending());
Abbas Hosseini
źródło