Bagaimana Anda melakukan kueri batas dalam JPQL atau HQL?


239

Di Hibernate 3, apakah ada cara untuk melakukan yang setara dengan batas MySQL berikut di HQL?

select * from a_table order by a_table_column desc limit 0, 20;

Saya tidak ingin menggunakan setMaxResults jika memungkinkan. Ini tentu saja dimungkinkan pada versi Hibernate / HQL yang lama, tetapi tampaknya telah menghilang.


2
Saya menggunakan Hibernate-5.0.12. Apakah ini masih belum tersedia? Akan sangat berat untuk mendapatkan satu juta atau lebih catatan dan kemudian menerapkan filter di atasnya- setMaxResultsseperti yang diperhatikan oleh @Rachel dalam jawaban oleh @skaffman.
Rajeev Ranjan

Jawaban:


313

Ini diposting di forum Hibernate beberapa tahun yang lalu ketika ditanya tentang mengapa ini bekerja di Hibernate 2 tetapi tidak di Hibernate 3:

Limit tidak pernah merupakan klausa yang didukung dalam HQL. Anda seharusnya menggunakan setMaxResults ().

Jadi jika itu bekerja di Hibernate 2, sepertinya itu kebetulan, bukan oleh desain. Saya pikir ini karena parser Hibernate 2 HQL akan menggantikan bit kueri yang dikenali sebagai HQL, dan membiarkan sisanya seperti semula, sehingga Anda dapat menyelinap ke dalam beberapa SQL asli. Hibernate 3, bagaimanapun, memiliki AST HQL Parser yang tepat, dan itu jauh lebih sedikit memaafkan.

Saya pikir Query.setMaxResults()ini satu-satunya pilihan Anda.


3
Saya berpendapat bahwa pendekatan Hibernate 3 lebih tepat. Penggunaan Hibernate Anda dimaksudkan sebagai agnostik basis data, jadi Anda harus melakukan hal-hal ini secara abstrak.
matt b

6
Saya setuju, tapi itu membuat migrasi adalah rasa sakit kerajaan di pantat ketika fitur dijatuhkan seperti itu.
skaffman

52
tetapi dengan setMaxResults, permintaan pertama dijalankan dan kemudian pada resultset yang Anda panggil setMaxResultsyang akan mengambil jumlah baris hasil yang terbatas dari resultset dan menampilkannya kepada pengguna, dalam kasus saya saya memiliki 3 juta catatan yang di-query dan kemudian memanggil setMaxResults untuk mengatur 50 catatan tetapi saya tidak ingin melakukan itu, sementara permintaan itu sendiri saya ingin meminta 50 catatan, apakah ada cara untuk melakukan itu?
Rachel

2
Posting lama saya tahu. Saya sepenuhnya setuju dengan Rachel. Menggunakan NHibernate (.Net port dari Hibernate), saya baru saja meningkatkan dari 2 menjadi 3 dan hal yang sama, top X sekarang melempar kesalahan parser. Namun, ketika saya menambahkan setMaxResults pada permintaan, itu memang menghasilkan TOP X di SQL yang dihasilkan (menggunakan MsSql2008Dialect). Ini bagus.
Thierry_S

17
@Rachel Dengan setMaxResultshibernate akan menambahkan limitbagian ke kueri. Itu tidak akan mendapatkan semua hasil. Anda dapat memeriksa kueri yang dihasilkannya dengan mengaktifkan:<property name="show_sql">true</property>
Andrejs

148
 // SQL: SELECT * FROM table LIMIT start, maxRows;

Query q = session.createQuery("FROM table");
q.setFirstResult(start);
q.setMaxResults(maxRows);

6
Saya suka yang terbaik ini karena setFirstResultsebenarnya disebutkan dalam jawaban ini sedangkan di sini dan di tempat lain mereka hanya mengatakan setMaxResultsini dan setMaxResultsitu tanpa menyebutkan cara mengatur offset.
demongolem

19

Jika Anda tidak ingin menggunakan setMaxResults()pada Queryobjek maka Anda bisa kembali selalu revert menggunakan SQL normal.


37
Itu tidak terlalu menarik.
stevedbrown

8
Saya juga tidak menemukan HQL yang menarik. Mengapa tidak menulis tampilan pada server DB Anda yang menerapkan batas dan kemudian meminta HQL untuk melihat tampilan itu: P
pjp

1
Itu hanya salah satu dari hal-hal itu, sementara SQL jauh lebih mudah daripada HQL untuk setiap permintaan, membuat tampilan dan menulis SQL asli cenderung tidak begitu bagus untuk refactoring. Saya mencoba menghindarinya ketika saya bisa. Masalah sebenarnya yang sebenarnya adalah saya salah menulis query MySQL saya dan berpikir setMaxResults menjadi aneh. Bukan itu.
stevedbrown

dan jika Anda mencoba untuk beralih antara vendor DBMS yang berbeda, rasa sakit sedang menunggu Anda.
Olgun Kaya

5

Jika Anda tidak ingin menggunakan setMaxResults, Anda juga dapat menggunakan Query.scroll sebagai ganti daftar, dan ambil baris yang Anda inginkan. Berguna untuk paging misalnya.


Terima kasih, jawaban yang diterima tidak memecahkan masalah bagi saya, karena setMaxResults()memuat pertama setiap catatan dalam memori dan kemudian membuat sublist, yang ketika ada ratusan ribu atau lebih catatan crash server, karena kehabisan memori. Namun saya bisa pergi dari permintaan mengetik JPA ke permintaan Hibernate melalui QueryImpl hibernateQuery = query.unwrap(QueryImpl.class)dan kemudian saya bisa menggunakan scroll()metode seperti yang Anda sarankan.
toongeorges

Setidaknya dengan dialek Oracle ini tidak benar (Hibernate menggunakan kolom virtual ROWNUM). Mungkin itu tergantung pada pengemudi. DB lain memiliki fungsi TOP.
Lluis Martinez

Permintaan saya menggunakan pengambilan gabungan. Ini menghasilkan peringatan Hibernate "firstResult / maxResults yang ditentukan dengan koleksi ambil; diterapkan dalam memori". Jadi dengan pengambilan gabungan, Hibernate memuat koleksi lengkap dalam memori. Menjatuhkan bergabung bukanlah pilihan karena alasan kinerja. Ketika saya menggunakan ScrollableResults, saya memiliki kontrol lebih besar tentang catatan mana yang dimuat dalam memori. Saya tidak dapat memuat semua catatan dengan ScrollableResults tunggal, karena ini juga menghasilkan kehabisan memori. Saya bereksperimen memuat banyak halaman dengan ScrollableResults yang berbeda. Jika ini tidak berhasil, saya akan menggunakan SQL.
toongeorges

Aneh, saya tidak pernah menemukan itu. Ya kadang-kadang menggunakan JDBC lurus adalah cara untuk melakukannya, khususnya untuk proses besar / batch.
Lluis Martinez

Hubungan @OneToMany menyebabkan masalah saya. Jika entah bagaimana saya bisa menjalankan fungsi agregat Oracle LISTAGG di Hibernate untuk menggabungkan beberapa nilai menjadi satu nilai, maka saya dapat menjatuhkan gabungan dan menggantinya dengan subquery.
toongeorges

2

Anda dapat dengan mudah menggunakan pagination untuk ini.

    @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value = "true") })
    @Query("select * from a_table order by a_table_column desc")
    List<String> getStringValue(Pageable pageable);

Anda harus lulus new PageRequest(0, 1)untuk mengambil catatan dan dari daftar mengambil catatan pertama.


2

Karena ini adalah pertanyaan yang sangat umum, saya menulis artikel ini , yang menjadi dasar jawaban ini.

The setFirstResultdan setMaxResults Querymetode

Untuk JPA dan Hibernate Query, setFirstResultmetode ini setara dengan OFFSET, dan setMaxResultsmetode ini setara dengan LIMIT:

List<Post> posts = entityManager
.createQuery(
    "select p " +
    "from Post p " +
    "order by p.createdOn ")
.setFirstResult(10)
.setMaxResults(10)
.getResultList();

The LimitHandlerabstraksi

Hibernate LimitHandlermendefinisikan logika pagination-spesifik-database, dan seperti yang diilustrasikan oleh diagram berikut, Hibernate mendukung banyak opsi pagination-spesifik-database:

Implementasi <code> LimitHandler </code>

Sekarang, tergantung pada sistem basis data relasional yang Anda gunakan, kueri JPQL di atas akan menggunakan sintaks pagination yang tepat.

MySQL

SELECT p.id AS id1_0_,
       p.created_on AS created_2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?, ?

PostgreSQL

SELECT p.id AS id1_0_,
       p.created_on AS created_2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?

SQL Server

SELECT p.id AS id1_0_,
       p.created_on AS created_on2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
OFFSET ? ROWS 
FETCH NEXT ? ROWS ONLY

Peramal

SELECT *
FROM (
    SELECT 
        row_.*, rownum rownum_
    FROM (
        SELECT 
            p.id AS id1_0_,
            p.created_on AS created_on2_0_,
            p.title AS title3_0_
        FROM post p
        ORDER BY p.created_on
    ) row_
    WHERE rownum <= ?
)
WHERE rownum_ > ?

Keuntungan menggunakan setFirstResultdan setMaxResultsadalah bahwa Hibernate dapat menghasilkan sintaks pagination khusus-database untuk setiap basis data relasional yang didukung.

Dan, Anda tidak terbatas pada permintaan JPQL saja. Anda bisa menggunakan setFirstResultdan setMaxResultsmetode tujuh untuk kueri SQL asli.

Kueri SQL asli

Anda tidak perlu melakukan hardcode pagination khusus basis data saat menggunakan query SQL asli. Hibernate dapat menambahkan itu ke kueri Anda.

Jadi, jika Anda menjalankan query SQL ini pada PostgreSQL:

List<Tuple> posts = entityManager
.createNativeQuery(
    "SELECT " +
    "   p.id AS id, " +
    "   p.title AS title " +
    "from post p " +
    "ORDER BY p.created_on", Tuple.class)
.setFirstResult(10)
.setMaxResults(10)
.getResultList();

Hibernate akan mengubahnya sebagai berikut:

SELECT p.id AS id,
       p.title AS title
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?

Keren kan?

Di luar pagination berbasis SQL

Pagination bagus ketika Anda dapat mengindeks kriteria pemfilteran dan pengurutan. Jika persyaratan halaman Anda menyiratkan penyaringan dinamis, itu pendekatan yang jauh lebih baik untuk menggunakan solusi indeks terbalik, seperti ElasticSearch.

Lihat artikel ini untuk lebih jelasnya.


1

String hql = "select userName from AccountInfo order by points desc 5";

Ini bekerja untuk saya tanpa menggunakan setmaxResults();

Berikan nilai maksimal di yang terakhir (dalam hal ini 5) tanpa menggunakan kata kunci limit. : P


7
Hm ... tidak terlalu yakin tentang ini, [rujukan?] Ini mungkin kecelakaan dan tiba-tiba bisa berhenti bekerja di versi baru hibernate.
Konrad 'ktoso' Malawski

4
Silakan merujuk ke dokumen resmi.
Lakukan Nhu Vy

1
Ini tidak berfungsi untuk saya di Hibernate Versi 5.2.12 Final
jDub9

1
Ini Tidak berfungsi, Tidak mendukung hibernate 4.x juga.
Faiz Akram

0

Pengamatan saya adalah bahwa meskipun Anda memiliki batas dalam HQL (hibernate 3.x), itu akan menyebabkan kesalahan parsing atau diabaikan. (jika Anda memesan + desc / asc sebelum batas, itu akan diabaikan, jika Anda tidak memiliki desc / asc sebelum batas, itu akan menyebabkan kesalahan penguraian)


0

Jika dapat mengatur batas dalam mode ini

public List<ExampleModel> listExampleModel() {
    return listExampleModel(null, null);
}

public List<ExampleModel> listExampleModel(Integer first, Integer count) {
    Query tmp = getSession().createQuery("from ExampleModel");

    if (first != null)
        tmp.setFirstResult(first);
    if (count != null)
        tmp.setMaxResults(count);

    return (List<ExampleModel>)tmp.list();
}

Ini adalah kode yang sangat sederhana untuk menangani batas atau daftar.


0
Criteria criteria=curdSession.createCriteria(DTOCLASS.class).addOrder(Order.desc("feild_name"));
                criteria.setMaxResults(3);
                List<DTOCLASS> users = (List<DTOCLASS>) criteria.list();
for (DTOCLASS user : users) {
                System.out.println(user.getStart());
            }

0

Anda perlu menulis kueri asli, lihat ini .

@Query(value =
    "SELECT * FROM user_metric UM WHERE UM.user_id = :userId AND UM.metric_id = :metricId LIMIT :limit", nativeQuery = true)
List<UserMetricValue> findTopNByUserIdAndMetricId(
    @Param("userId") String userId, @Param("metricId") Long metricId,
    @Param("limit") int limit);

0

Anda dapat menggunakan kueri di bawah ini

NativeQuery<Object[]> query = session.createNativeQuery(select * from employee limit ?)
query.setparameter(1,1);

0

Cuplikan di bawah ini digunakan untuk melakukan kueri batas menggunakan HQL.

Query query = session.createQuery("....");
query.setFirstResult(startPosition);
query.setMaxResults(maxRows);

Anda bisa mendapatkan aplikasi demo di tautan ini .


-1
@Query(nativeQuery = true,
       value = "select from otp u where u.email =:email order by u.dateTime desc limit 1")
public List<otp> findOtp(@Param("email") String email);
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.