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

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.


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

}

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)

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

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

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>.


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

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());
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.