setMaxResults untuk penjelasan Spring-Data-JPA?

127

Saya mencoba untuk memasukkan Spring-Data-JPA ke dalam proyek saya. Satu hal yang membingungkan saya adalah bagaimana cara mencapai setMaxResults (n) dengan anotasi?

misalnya, kode saya:

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

Saya hanya perlu mengembalikan one (and only one)Pengguna dari otherObj, tetapi saya tidak dapat menemukan cara untuk menganotasi maxResults. Bisakah seseorang memberi saya petunjuk?

(mysql mengeluh:

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

)

Saya menemukan tautan: https://jira.springsource.org/browse/DATAJPA-147 , saya mencoba tetapi gagal. Sepertinya tidak mungkin sekarang? Mengapa fitur sepenting itu tidak disertakan dalam Spring-Data?

Jika saya menerapkan fitur ini secara manual:

public class UserRepositoryImpl implements UserRepository

Saya harus menerapkan banyak metode yang telah ditentukan sebelumnya CrudRepository, ini akan mengerikan.

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

smallufo
sumber

Jawaban:

176

Pada Spring Data JPA 1.7.0 (kereta rilis Evans).

Anda dapat menggunakan kata kunci Topdan yang baru diperkenalkan Firstyang memungkinkan Anda untuk menentukan metode kueri seperti ini:

findTop10ByLastnameOrderByFirstnameAsc(String lastname);

Spring Data akan secara otomatis membatasi hasil ke angka yang Anda tentukan (default ke 1 jika dihilangkan). Perhatikan bahwa urutan hasil menjadi relevan di sini (baik melalui OrderByklausa seperti yang terlihat dalam contoh atau dengan menyerahkan Sortparameter ke dalam metode). Baca lebih lanjut tentang itu di entri blog yang membahas fitur-fitur baru dari rangkaian rilis Spring Data Evans atau dalam dokumentasi .

Untuk versi sebelumnya

Untuk mengambil hanya potongan data, Spring Data menggunakan abstraksi pagination yang dilengkapi dengan Pageableantarmuka di sisi yang meminta serta Pageabstraksi di sisi hasil. Jadi Anda bisa mulai dengan

public interface UserRepository extends Repository<User, Long> {

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

dan gunakan seperti ini:

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

Jika Anda perlu mengetahui konteks hasil (halaman mana yang sebenarnya? Apakah yang pertama? Berapa total totalnya?), Gunakan Pagesebagai tipe kembalian:

public interface UserRepository extends Repository<User, Long> {

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

Kode klien kemudian dapat melakukan sesuatu seperti ini:

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

Bukan berarti kami akan memicu proyeksi hitungan dari kueri yang sebenarnya untuk dieksekusi jika Anda menggunakan Pagetipe pengembalian karena kami perlu mencari tahu berapa banyak elemen yang ada secara total untuk menghitung metadata. Selain itu, pastikan Anda benar-benar melengkapi PageRequestdengan informasi sortir untuk mendapatkan hasil yang stabil. Jika tidak, Anda mungkin memicu kueri dua kali dan mendapatkan hasil yang berbeda bahkan tanpa perubahan data di bawahnya.

Oliver Drotbohm
sumber
14
Terima kasih, tapi saya masih berharap untuk solusi 'berbasis anotasi'. Karena 'Page / Pageable' terlalu berlebihan dalam kasus ini. Dan klien harus membuat Pageable / Page untuk mengambil hasil 'satu dan hanya satu' ... Ini sangat tidak ramah untuk digunakan. Dan sepertinya Anda bertanggung jawab atas Spring-Data, saya harap Anda dapat melihat lebih jauh masalah ini: stackoverflow.com/questions/9276461/… . Bisakah Anda mengonfirmasi apakah itu bug Spring-Data?
smallufo
1
+ ini berfungsi, terima kasih tetapi solusi berbasis anotasi akan lebih baik di versi yang lebih baru
azerafati
1
kueri hitungan dijalankan di sini yang sama sekali tidak diperlukan jika kita menginginkan kueri terbatas.
azerafati
1
Ini tidak jika Anda menggunakan Listsebagai tipe kembalian dari metode tersebut.
Oliver Drotbohm
3
Menurut saya, Topdan Firstkata kunci tidak berlaku untuk metode yang menggunakan @Queryanotasi? Hanya mereka, yang menggunakan pembuatan kueri berbasis nama metode?
Ruslan Stelmachenko
106

Jika Anda menggunakan Java 8 dan Spring Data 1.7.0, Anda dapat menggunakan metode default jika Anda ingin menggabungkan @Queryanotasi dengan pengaturan hasil yang maksimal:

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
sumber
3
berpotensi berguna untuk hasil tunggalStream<User> ... {...} default Optional<User> ... { ...findAny() }
xenoterracide
28

Ada cara untuk memberikan padanan dengan "setMaxResults (n) by annotation" seperti berikut:

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

Ini akan berhasil, ketika sebuah halaman dikirim sebagai parameter. Misalnya untuk mengambil 10 record pertama Anda perlu mengatur pageable ke nilai ini:

new PageRequest(0, 10)
ncaralicea.dll
sumber
Jika Anda menggunakan Pagable maka Kembalinya Halaman <XYZ> bukan Daftar <XYZ>
Arundev
@Arundev salah - Listbekerja dengan sempurna (dan menghindari countpertanyaan yang tidak perlu seperti yang telah disebutkan dalam komentar sebelumnya )
Janaka Bandara
21

Gunakan Spring Data Evans (RELEASE 1.7.0)

rilis baru Spring Data JPA dengan daftar modul lain yang disebut Evans memiliki fitur menggunakan kata kunci Top20dan Firstuntuk membatasi hasil kueri,

jadi sekarang Anda bisa menulis

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

atau

List<User> findTop20ByLastnameOrderByIdDesc(String lastname);

atau untuk satu hasil

List<User> findFirstByLastnameOrderByIdDesc(String lastname);
azerafati.dll
sumber
5
Opsional <User> findFirstByLastnameOrderByIdDesc (String lastname);
krmanish007
Setelah mendapatkan 20 hasil teratas, Bagaimana cara mendapatkan 20 hasil berikutnya saat saya membuka halaman berikutnya di situs web menggunakan pagination?
kittu
@kittu, itu tentu saja pertanyaan lain tetapi Anda harus memberikan Pageableobjek. lihat dokumentasi musim semi
azerafati
mengapa mereka membuat metode mengembalikan Daftar sialan jika ditentukan PERTAMA ??
Vasile Surdu
13

Pilihan terbaik bagi saya adalah kueri asli:

@Query(value="SELECT * FROM users WHERE other_obj = ?1 LIMIT 1", nativeQuery = true)
User findByOhterObj(OtherObj otherObj);
Grigory Kislin
sumber
Tidak yakin apakah batas didukung dalam Hibernate: forum.hibernate.org/viewtopic.php?f=9&t=939314
Witold Kaczurba
2
Hancurkan seluruh konsep! Lebih baik gunakan EntityManager sebagai gantinya!
Spektakulatius
@ Code.IT Saya menyebarkan konsep KISS. Selain itu nativeQuery parameter anotasi JPA ditambahkan bukan untuk diabaikan.
Grigory Kislin
@GKislin daripada Anda harus mengganti nama metode Anda seperti findLimitOneByOtherObj. Anda juga lupa menambahkan OrderBy yang mengarah ke hasil yang salah jika other_obj tidak unik. Jika tidak, pernyataan di mana pada kolom unik harus cukup tanpa batas satu
Spektakulatius
LIMIT kata kunci bukan hibernate tetapi postgres / mysql
Acewin
5

Jika kelas Anda @Repositorymeluas, JpaRepositoryAnda dapat menggunakan contoh di bawah ini.

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

getContent mengembalikan a List<Transaction>.

Luis Camargo
sumber
bagaimana jika saya memiliki 2 kondisi di mana dalam kueri seperti, transactionRepository.findByFirstNameAndLastName (firstname, lastname)? apakah itu akan berhasil? Saya mendapatkan pengecualian ini org.springframework.data.mapping.PropertyReferenceException: Tidak ada pembuatan properti yang ditemukan untuk jenis Board.
Shailesh
4

Ini juga dimungkinkan menggunakan @QueryHints. Contoh di bawah ini menggunakan 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
sumber
Saya telah menguji kode ini tetapi tidak berfungsi. Tak terkecuali, namun tak ada hasil yang pantas lebih rapi. Juga dokumentasinya sangat tipis tentang QueryHints. Lihat di bawah. docs.spring.io/spring-data/jpa/docs/1.7.1.RELEASE/reference/…
bogdan.rusu
IIRC, tidak ada jaminan bahwa petunjuk kueri akan diikuti. Ini hanya cara untuk meneruskan "rekomendasi ramah" ke implementasi.
Priidu Neemre
Saya mencobanya @QueryHints({@QueryHint(name = org.hibernate.annotations.FETCH_SIZE, value = "1")})dalam mode Hibernate, tetapi alias :( tidak berfungsi
Grigory Kislin
2
org.hibernate.annotations.FETCH_SIZE ukuran pengambilannya, bukan batasnya, bukan hasil maksimalnya. Hiber cant
Zhuravskiy Vitaliy
4

new PageRequest(0,10)tidak berfungsi di versi Spring yang lebih baru (saya menggunakan 2.2.1.RELEASE). Pada dasarnya, konstruktor mendapat parameter tambahan sebagai Sorttipe. Selain itu, konstruktornya protectedjadi Anda harus menggunakan salah satu kelas turunannya atau memanggil ofmetode statisnya:

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

Anda juga dapat menghilangkan penggunaan Sortparameter dan secara implisit menggunakan pengurutan default (urutkan berdasarkan pk, dll.):

PageRequest.of(0, 10)

Deklarasi fungsi Anda harus seperti ini:

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

dan fungsinya adalah:

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