Wie macht man eine Limit-Abfrage in JPQL oder HQL?

239

Gibt es in Hibernate 3 eine Möglichkeit, das Äquivalent des folgenden MySQL-Limits in HQL zu erreichen?

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

Ich möchte setMaxResults wenn möglich nicht verwenden. Dies war in der älteren Version von Hibernate / HQL definitiv möglich, scheint aber verschwunden zu sein.

Strohbraun
quelle
2
Ich benutze Hibernate-5.0.12. Ist das noch nicht verfügbar? Es wäre sehr schwer, setMaxResultsungefähr eine Million Datensätze zu erhalten und dann den Filter darauf anzuwenden , wie @Rachel in der Antwort von @skaffman bemerkt hat.
Rajeev Ranjan

Antworten:

313

Dies wurde vor einigen Jahren im Hibernate-Forum veröffentlicht, als gefragt wurde, warum dies in Hibernate 2, aber nicht in Hibernate 3 funktioniert:

Limit war in HQL nie eine unterstützte Klausel. Sie sollen setMaxResults () verwenden.

Wenn es also in Hibernate 2 funktioniert hat, scheint dies eher zufällig als beabsichtigt zu sein. Ich denke, das lag daran, dass der Hibernate 2-HQL-Parser die Bits der Abfrage, die er als HQL erkannte, ersetzen und den Rest so lassen würde, wie er war, damit Sie sich in natives SQL einschleichen können. Hibernate 3 hat jedoch einen richtigen AST-HQL-Parser und ist viel weniger verzeihend.

Ich denke Query.setMaxResults()wirklich ist deine einzige Option.

Skaffman
quelle
3
Ich würde argumentieren, dass der Ansatz von Hibernate 3 korrekter ist. Ihre Verwendung von Hibernate soll datenbankunabhängig sein, daher sollten Sie diese Art von Dingen abstrakt ausführen müssen.
Matt B
6
Ich stimme zu, aber es macht Migration zu einem königlichen Schmerz im Arsch, wenn Features so fallen gelassen werden.
Skaffman
52
aber mit setMaxResults wird zuerst eine Abfrage ausgeführt und dann auf der von Ihnen aufgerufenen setMaxResultsErgebnismenge eine begrenzte Anzahl von Ergebniszeilen aus der Ergebnismenge entnommen und dem Benutzer angezeigt. In meinem Fall habe ich 3 Millionen Datensätze, die abgefragt werden, und rufe dann setMaxResults auf, um sie festzulegen 50 Datensätze, aber ich möchte das nicht tun, während ich selbst 50 Datensätze abfragen möchte, gibt es eine Möglichkeit, das zu tun?
Rachel
2
Alter Beitrag, den ich kenne. Ich stimme Rachel voll und ganz zu. Mit NHibernate (.Net-Port von Hibernate) habe ich kürzlich ein Upgrade von 2 auf 3 durchgeführt. Top X gibt jetzt einen Parserfehler aus. Als ich der Abfrage jedoch setMaxResults hinzufügte, wurde in der resultierenden SQL ein TOP X generiert (unter Verwendung von MsSql2008Dialect). Das ist gut.
Thierry_S
17
@Rachel With setMaxResultsHibernate hängt den limitTeil an die Abfrage an. Es werden nicht alle Ergebnisse angezeigt. Sie können die Abfrage überprüfen, die es erzeugt, indem Sie aktivieren:<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);
Jessu
quelle
6
Ich mag dieses am besten, weil setFirstResultes in dieser Antwort tatsächlich erwähnt wird, während hier und anderswo nur setMaxResultsdies und setMaxResultsdas gesagt wird , ohne zu erwähnen, wie der Offset eingestellt werden soll.
Demongolem
19

Wenn Sie setMaxResults()das QueryObjekt nicht verwenden möchten, können Sie jederzeit wieder normales SQL verwenden.

pjp
quelle
37
Das ist gar nicht so aufregend.
Staubraun
8
Ich finde HQL auch nicht aufregend. Warum nicht eine Ansicht auf Ihren DB-Server schreiben, die das Limit anwendet, und dann HQL veranlassen, sich diese Ansicht anzusehen: P
pjp
1
Dies ist nur eines dieser Dinge, während SQL für jede Abfrage viel einfacher als HQL ist. Das Erstellen von Ansichten und das Schreiben von nativem SQL ist für das Refactoring in der Regel nicht so gut. Ich versuche es zu vermeiden, wenn ich kann. Das eigentliche Problem war, dass ich meine MySQL-Abfrage sowieso falsch geschrieben habe und setMaxResults für komisch hielt. Es war nicht.
Staubraun
und wenn Sie versuchen, zwischen verschiedenen DBMS-Anbietern zu wechseln, wartet Schmerz auf Sie.
Olgun Kaya
5

Wenn Sie setMaxResults nicht verwenden möchten, können Sie auch Query.scroll anstelle von list verwenden und die gewünschten Zeilen abrufen. Nützlich zum Beispiel zum Blättern.

Lluis Martinez
quelle
Vielen Dank, die akzeptierte Antwort hat das Problem für mich nicht gelöst, da setMaxResults()zuerst jeder Datensatz im Speicher geladen und dann eine Unterliste erstellt wird. Wenn hunderttausende oder mehr Datensätze vorhanden sind, stürzt der Server ab, weil nicht genügend Speicher vorhanden ist. Ich könnte jedoch von einer JPA-typisierten Abfrage zu einer Hibernate-Abfrage übergehen QueryImpl hibernateQuery = query.unwrap(QueryImpl.class)und dann die scroll()von Ihnen vorgeschlagene Methode verwenden.
Toongeorges
Zumindest mit Oracle-Dialekt ist dies nicht der Fall (Hibernate verwendet die virtuelle Spalte ROWNUM). Vielleicht hängt es vom Fahrer ab. Andere DBs haben die TOP-Funktion.
Lluis Martinez
Meine Abfrage verwendet einen Join-Abruf. Dies führt zu der Warnung "Ruhezustand" "firstResult / maxResults", die mit dem Abruf der Sammlung angegeben wurde; Anwendung im Speicher ". Mit einem Join-Abruf lädt Hibernate die gesamte Sammlung in den Speicher. Das Löschen des Joins ist aus Leistungsgründen keine Option. Wenn ich ScrollableResults verwende, habe ich mehr Kontrolle darüber, welche Datensätze in den Speicher geladen werden. Ich kann nicht alle Datensätze mit einem einzigen ScrollableResults laden, da dies auch zu wenig Speicher führt. Ich experimentiere mit dem Laden mehrerer Seiten mit verschiedenen ScrollableResults. Wenn dies nicht funktioniert, werde ich mit SQL gehen.
Toongeorges
Das ist komisch, das habe ich noch nie erlebt. Ja, manchmal ist die Verwendung von direktem JDBC der richtige Weg, insbesondere für Massive / Batch-Prozesse.
Lluis Martinez
Die @ OneToMany-Beziehungen verursachen meine Probleme. Wenn ich irgendwie die Oracle-Aggregatfunktion LISTAGG im Ruhezustand ausführen könnte, um die mehreren Werte zu einem einzigen Wert zu verketten, kann ich die Verknüpfungen löschen und durch eine Unterabfrage ersetzen.
Toongeorges
2

Sie können hierfür leicht die Paginierung verwenden.

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

Sie müssen übergeben new PageRequest(0, 1), um Datensätze abzurufen, und aus der Liste den ersten Datensatz abrufen.

tk_
quelle
2

Da dies eine sehr häufige Frage ist, habe ich diesen Artikel geschrieben , auf dem diese Antwort basiert.

Die setFirstResultund setMaxResults QueryMethoden

Für eine JPA und Ruhezustand Query, das setFirstResultist das Äquivalent von Verfahren OFFSETund das setMaxResultsVerfahren ist das Äquivalent von LIMIT:

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

Die LimitHandlerAbstraktion

Der Ruhezustand LimitHandlerdefiniert die datenbankspezifische Paginierungslogik. Wie das folgende Diagramm zeigt, unterstützt der Ruhezustand viele datenbankspezifische Paginierungsoptionen:

<code> LimitHandler </ code> -Implementierungen

Abhängig vom zugrunde liegenden relationalen Datenbanksystem, das Sie verwenden, verwendet die obige JPQL-Abfrage nun die richtige Paginierungssyntax.

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

Orakel

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

Der Vorteil von setFirstResultund setMaxResultsbesteht darin, dass Hibernate die datenbankspezifische Paginierungssyntax für alle unterstützten relationalen Datenbanken generieren kann.

Sie sind nicht nur auf JPQL-Abfragen beschränkt. Sie können die setFirstResultund setMaxResults-Methode sieben für native SQL-Abfragen verwenden.

Native SQL-Abfragen

Sie müssen die datenbankspezifische Paginierung nicht fest codieren, wenn Sie native SQL-Abfragen verwenden. Der Ruhezustand kann dies zu Ihren Abfragen hinzufügen.

Wenn Sie diese SQL-Abfrage unter PostgreSQL ausführen:

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

Der Ruhezustand wird es wie folgt transformieren:

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

Cool, oder?

Jenseits der SQL-basierten Paginierung

Die Paginierung ist gut, wenn Sie die Filter- und Sortierkriterien indizieren können. Wenn Ihre Paginierungsanforderungen eine dynamische Filterung implizieren, ist es viel besser, eine Lösung mit invertiertem Index wie ElasticSearch zu verwenden.

Weitere Informationen finden Sie in diesem Artikel .

Vlad Mihalcea
quelle
1

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

Das hat bei mir ohne Verwendung funktioniert setmaxResults();

Geben Sie einfach den Maximalwert im letzten (in diesem Fall 5) an, ohne das Schlüsselwort zu verwenden limit. : P.

Dilawar
quelle
7
Hm ... ich bin mir nicht sicher, dies könnte ein Unfall sein und könnte plötzlich aufhören, in einer neuen Version des Ruhezustands zu arbeiten.
Konrad 'ktoso' Malawski
4
Bitte beziehen Sie sich auf das offizielle Dokument.
Do Nhu Vy
1
Dies funktioniert nicht für mich in Hibernate Version 5.2.12 Final
jDub9
1
Es funktioniert nicht, unterstützt auch den Ruhezustand 4.x nicht.
Faiz Akram
0

Meine Beobachtung ist, dass selbst wenn Sie ein Limit in der HQL (Ruhezustand 3.x) haben, dies entweder einen Analysefehler verursacht oder einfach ignoriert wird. (Wenn Sie eine Reihenfolge von + desc / asc vor dem Limit haben, wird diese ignoriert. Wenn Sie nicht desc / asc vor dem Limit haben, führt dies zu einem Analysefehler.)

Xingsheng
quelle
0

Wenn kann ein Limit in diesem Modus verwalten

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

Dies ist ein wirklich einfacher Code, um ein Limit oder eine Liste zu behandeln.

Biancardi
quelle
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());
            }
Deep Chand Jaipur
quelle
0

Sie müssen eine native Abfrage schreiben, verweisen Sie darauf .

@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);
Swatisinghi
quelle
0

Sie können die folgende Abfrage verwenden

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

Das folgende Snippet wird verwendet, um eine Limit-Abfrage mit HQL durchzuführen.

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

Sie können die Demo-Anwendung unter diesem Link erhalten .

Hari Krishna
quelle
-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);
Leena Varshney
quelle