JPQL veya HQL'de bir sınır sorgusu nasıl yapılır?


239

Hazırda Bekletme 3'te, HQL'de aşağıdaki MySQL sınırının eşdeğerini yapmanın bir yolu var mı?

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

Mümkünse setMaxResults'u kullanmak istemiyorum. Bu, Hibernate / HQL'in eski sürümünde kesinlikle mümkün oldu, ancak kayboldu gibi görünüyor.


2
Kullanıyorum Hibernate-5.0.12. Bu hala mevcut değil mi? Bir milyon kadar kayıt almak ve daha sonra filtreyi üzerine uygulamak, setMaxResults@kaffman'ın cevabında @Rachel tarafından fark edildiği gibi gerçekten ağır olurdu .
Rajeev Ranjan

Yanıtlar:


313

Bu, Hibernate 3'te neden Hibernate 2'de çalıştığını ancak Hibernate 3'te çalışmadığını sorduğunda birkaç yıl önce Hibernate forumuna gönderildi:

Sınır, HQL'de hiçbir zaman desteklenen bir yan tümce değildi . SetMaxResults () yöntemini kullanmanız amaçlanmıştır.

Eğer Hibernate 2'de çalıştıysa, tasarımdan ziyade tesadüf eseri görünüyor. Ben düşünüyorum bazı yerli SQL gizlice böylece hazırda 2 HQL ayrıştırıcı, bu HQL olarak kabul Söz konusu sorgu parçalarını değiştirmek ve eskisi gibi gerisini bırakın çünkü bu oldu. Hibernate 3, uygun bir AST HQL Parser'a sahiptir ve çok daha az affedicidir.

Query.setMaxResults()Gerçekten tek seçeneğin olduğunu düşünüyorum .


3
Hibernate 3'ün yaklaşımının daha doğru olduğunu iddia ediyorum. Hazırda Bekletme kullanımınızı veritabanı agnostik olması amaçlanmıştır, bu nedenle bu tür şeyleri soyut bir şekilde yapmanız gerekir.
matt b

6
Kabul ediyorum, ancak özellikler böyle düştüğünde göçün kıçta kraliyet ağrısı olduğunu gösteriyor.
skaffman

52
ancak setMaxResults ile, ilk sorgu çalıştırılır ve daha sonra, sonuç kümesinden sonuç kümesinden setMaxResultssınırlı sayıda sonuç satırı alıp kullanıcıya görüntüleyeceğiniz sonuç kümesinde, benim durumumda sorgulanan ve daha sonra ayarlamak için setMaxResults çağırıyorum 3 milyon kayıt var 50 kayıtları ama bunu yapmak istemiyorum, sorgu kendisi iken 50 kayıtları sorgulamak istiyorum, bunu yapmanın bir yolu var mı?
Rachel

2
Eski yazı biliyorum. Rachel'a tamamen katılıyorum. NHibernate'i (Hibernate'in .Net portu) kullanarak, yakın zamanda 2'den 3'e yükselttim ve aynı şey, top X şimdi ayrıştırıcı hatası atıyor. Ancak, sorguya setMaxResults eklediğimde, ortaya çıkan SQL'de bir TOP X üretti (MsSql2008Dialect kullanarak). Bu iyi.
Thierry_S

17
@Rachel setMaxResultsHazırda bekletme ile limitparçayı sorguya ekler . Tüm sonuçları alamayacak. Ürettiği sorguyu etkinleştirerek kontrol edebilirsiniz:<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
Bunu en çok sevdim çünkü setFirstResultaslında bu cevapta bahsedilirken, burada ve başka yerlerde sadece bunu söylüyorlar setMaxResultsve setMaxResultsofseti nasıl ayarlayacağından bahsetmeden.
demongolem

19

Kullanmak istemiyorsanız setMaxResults()üzerinde Querynesnenin o zaman olabilir, normal SQL kullanarak her zaman geri dönme eğilimi sırt.


37
Bu o kadar da heyecan verici değil.
stevedbrown

8
HQL'i de heyecan verici bulmuyorum. Neden DB sunucunuzda sınırı uygulayan bir görünüm
yazıp HQL'i

1
SQL, her sorgu için HQL'den çok daha kolay olsa da, görünümler oluşturmak ve yerel SQL yazmak yeniden düzenleme için çok iyi değildir. Yapabildiğim zaman bundan kaçınmaya çalışıyorum. Bu asıl sorun, MySQL sorgumu yanlış yazdım ve setMaxResults'un garip olduğunu düşündüm. Öyle değildi.
stevedbrown

ve farklı DBMS satıcıları arasında geçiş yapmaya çalışırsanız, acı sizi bekliyor.
Olgun Kaya

5

SetMaxResults'u kullanmak istemiyorsanız, liste yerine Query.scroll öğesini kullanabilir ve istediğiniz satırları getirebilirsiniz. Örneğin sayfalama için kullanışlıdır.


Teşekkürler, kabul edilen cevap benim için sorunu çözmedi, çünkü setMaxResults()önce bellekteki her kaydı yükler ve daha sonra yüz binlerce veya daha fazla kayıt olduğunda sunucunun çökmesine neden olduğu bir alt liste oluşturur. Ancak üzerinden bir Hazırda Bekletme sorgusuna JPA yazılan sorgudan gidebilir QueryImpl hibernateQuery = query.unwrap(QueryImpl.class)ve sonra scroll()önerildiği gibi yöntemi kullanabilirsiniz .
toongeorges

En azından Oracle lehçesi ile bu doğru değildir (Hazırda Beklet, ROWNUM sanal sütununu kullanır). Belki sürücüye bağlıdır. Diğer DB'ler TOP işlevine sahiptir.
Lluis Martinez

Sorgum bir birleştirme getirme kullanıyor. Bu, Hazırda Bekletme uyarısı ile sonuçlanır "firstResult / maxResults toplama getirme ile belirtildi; bellekte uygulama". Birleştirme getirme ile Hibernate tüm koleksiyonu belleğe yüklüyor. Birleştirmeyi bırakmak, performans nedenlerinden dolayı bir seçenek değildir. ScrollableResults kullandığımda, hangi kayıtların bellekte yüklü olduğu hakkında daha fazla denetimim var. Tüm kayıtları tek bir ScrollableResults ile yükleyemiyorum, çünkü bu da bellek yetersizliğine neden oluyor. Farklı ScrollableResults ile birden fazla sayfa yüklemeyi deniyorum. Bu işe yaramazsa, SQL ile gideceğim.
toongeorges

Bu garip, hiç karşılaşmadım. Evet bazen düz JDBC kullanmak, özellikle büyük / toplu işlemler için kullanmanın yoludur.
Lluis Martinez

@OneToMany ilişkileri sorunlarıma neden oluyor. Bir şekilde birden çok değeri tek bir değere birleştirmek için Hibernate'de Oracle toplama işlevi LISTAGG'yi yürütebilirsem, birleştirmeleri bırakıp bir alt sorgu ile değiştirebilirim.
toongeorges

2

Bunun için sayfalandırmayı kolayca kullanabilirsiniz.

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

new PageRequest(0, 1)kayıtları getirmeye ve listeden ilk kaydı getirmeye geçmek zorundasınız .


2

Bu çok yaygın bir soru olduğundan, bu cevabın dayandığı bu makaleyi yazdım .

setFirstResultve setMaxResults Queryyöntemler

Bir JPA ve Hazırda Bekletme için Query, setFirstResultyöntem LIMIT eşdeğeridir OFFSETve setMaxResultsyöntem LIMIT eşdeğeridir:

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

LimitHandlersoyutlama

Hazırda Beklet LimitHandler, veritabanına özgü sayfalandırma mantığını tanımlar ve aşağıdaki diyagramda gösterildiği gibi Hazırda Beklet, veritabanına özgü birçok sayfalandırma seçeneğini destekler:

<code> LimitHandler </code> uygulamaları

Şimdi, kullandığınız temel ilişkisel veritabanı sistemine bağlı olarak, yukarıdaki JPQL sorgusu uygun sayfalandırma sözdizimini kullanacaktır.

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

torpil

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_ > ?

Kullanmanın avantajı setFirstResultve setMaxResultshazırda desteklenen herhangi bir ilişkisel veri tabanları için bir veritabanı özgü sayfalama sözdizimi türetebilmesidir.

Ayrıca, yalnızca JPQL sorgularıyla sınırlı değilsiniz. Yerel SQL sorguları için setFirstResultve setMaxResultsyöntem yedi'yi kullanabilirsiniz .

Yerel SQL sorguları

Yerel SQL sorgularını kullanırken veritabanına özgü sayfalandırmayı sabit olarak kodlamanız gerekmez. Hazırda Beklet, bunu sorgularınıza ekleyebilir.

Bu nedenle, bu SQL sorgusunu PostgreSQL'de yürütüyorsanız:

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

Hazırda Bekletme modu aşağıdaki gibi dönüştürür:

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

Harika, değil mi?

SQL tabanlı sayfalandırmanın ötesinde

Filtreleme ve sıralama ölçütlerini dizine ekleyebildiğinizde sayfalandırma iyidir. Sayfalandırma gereksinimleriniz dinamik filtreleme gerektiriyorsa, Elastik Arama gibi tersine çevrilmiş dizin çözümü kullanmak çok daha iyi bir yaklaşımdır.

Daha fazla ayrıntı için bu makaleye göz atın .


1

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

Bu kullanmadan benim için çalıştı setmaxResults();

Anahtar kelimeyi kullanmadan yalnızca son değeri (bu örnekte 5) girin limit. : P


7
Hm ... bundan emin değilim, [alıntı gerekli] bu bir kaza olabilir ve aniden hazırda bekletme modunun yeni bir sürümünde çalışmayı durdurabilir.
Konrad 'ktoso' Malawski

4
Lütfen resmi belgeye bakınız.
Nhu Vy

1
Hibernate Sürüm 5.2.12 Finalinde benim için çalışmıyor
jDub9

1
Çalışmıyor, hazırda bekletme 4.x'i de desteklemiyor.
Faiz Akram

0

Benim gözlemim, HQL'de (hazırda bekletme 3.x) sınırınız olsa bile, ayrıştırma hatasına neden olacak veya sadece göz ardı edilecektir. (sınırdan önce + desc / asc ile siparişiniz varsa, yok sayılır, sınırdan önce desc / asc yoksa, ayrıştırma hatasına neden olur)


0

Bu modda bir sınırı yönetebiliyorsa

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

Bu, bir sınır veya listeyi işlemek için gerçekten basit bir koddur.


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

Sen bakın yerli sorgu yazmak gerekir bu .

@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

Aşağıdaki sorguyu kullanabilirsiniz

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

0

Aşağıdaki snippet, HQL kullanarak sınır sorgusu gerçekleştirmek için kullanılır.

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

Demo uygulamasını bu linkten alabilirsiniz .


-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);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.