คุณจะค้นหาแบบ จำกัด ใน JPQL หรือ HQL ได้อย่างไร


239

ใน Hibernate 3 มีวิธีทำข้อ จำกัด MySQL ต่อไปนี้ใน HQL หรือไม่?

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

ฉันไม่ต้องการใช้ setMaxResults ถ้าเป็นไปได้ สิ่งนี้เป็นไปได้แน่นอนใน Hibernate / HQL รุ่นเก่า แต่ดูเหมือนว่าจะหายไป


2
Hibernate-5.0.12ฉันใช้ ยังไม่พร้อมใช้งานหรือไม่ มันจะหนักมากที่จะได้รับหนึ่งล้านแผ่นเสียงจากนั้นจึงใช้ตัวกรองกับมันsetMaxResultsมากกว่าที่สังเกตโดย @Rachel ในคำตอบของ @skaffman
Rajeev Ranjan

คำตอบ:


313

สิ่งนี้ถูกโพสต์บนฟอรัมไฮเบอร์เนตเมื่อไม่กี่ปีก่อนเมื่อถูกถามเกี่ยวกับสาเหตุที่ทำงานใน Hibernate 2 แต่ไม่ได้อยู่ใน Hibernate 3:

ข้อ จำกัดไม่เคยเป็นประโยคที่สนับสนุนใน HQL คุณตั้งใจจะใช้ setMaxResults ()

ดังนั้นถ้ามันทำงานในไฮเบอร์เนต 2 ดูเหมือนว่าเป็นเรื่องบังเอิญมากกว่าการออกแบบ ฉันคิดว่าเป็นเพราะตัวแยกวิเคราะห์ Hibernate 2 HQL จะแทนที่บิตของแบบสอบถามที่ได้รับการยอมรับว่าเป็น HQL และออกจากที่เหลือตามเดิมดังนั้นคุณสามารถแอบเข้าไปใน SQL ดั้งเดิมบางตัวได้ Hibernate 3 มีตัวแยกวิเคราะห์ AST HQL ที่เหมาะสมและมีการให้อภัยน้อยกว่ามาก

ฉันคิดว่าQuery.setMaxResults()เป็นตัวเลือกเดียวของคุณ


3
ฉันจะยืนยันว่าวิธีการของไฮเบอร์เนต 3 นั้นถูกต้องมากกว่า การใช้ Hibernate ของคุณนั้นมีความหมายว่าเป็นผู้ไม่เชื่อเรื่องฐานข้อมูลดังนั้นคุณควรจะทำสิ่งต่าง ๆ เหล่านี้ในลักษณะที่เป็นนามธรรม
30294 b ด้าน Matt

6
ฉันเห็นด้วย แต่มันทำให้การโยกย้ายเป็นความเจ็บปวดในตูดเมื่อคุณสมบัติถูกทิ้งเช่นนั้น
skaffman

52
แต่ด้วย setMaxResults แบบสอบถามแรกจะถูกเรียกใช้จากนั้นบน resultset ที่คุณเรียกใช้setMaxResultsซึ่งจะ จำกัด จำนวนแถวผลลัพธ์จาก resultset และแสดงให้ผู้ใช้เห็นในกรณีของฉันฉันมี 3 ล้านระเบียนซึ่งถูกสอบถามแล้วเรียก setMaxResults เพื่อตั้งค่า 50 บันทึก แต่ฉันไม่ต้องการที่จะทำในขณะที่แบบสอบถามเองฉันต้องการสอบถาม 50 บันทึกมีวิธีการทำเช่นนั้น?
Rachel

2
โพสต์เก่าฉันรู้ ฉันเห็นด้วยกับราเชลอย่างเต็มที่ ใช้ NHibernate (.Net พอร์ตของ Hibernate) ฉันเพิ่งอัพเกรดจาก 2 เป็น 3 และสิ่งเดียวกันตอนนี้ X บนกำลังขว้างข้อผิดพลาดในการแยกวิเคราะห์ อย่างไรก็ตามเมื่อฉันเพิ่ม setMaxResults บนเคียวรีมันสร้าง TOP X ใน SQL ผลลัพธ์ (โดยใช้ MsSql2008Dialect) ดีจัง.
Thierry_S

17
@Rachel ด้วยsetMaxResultsHibernate จะผนวกlimitส่วนหนึ่งของแบบสอบถาม มันจะไม่ได้รับผลลัพธ์ทั้งหมด คุณสามารถตรวจสอบแบบสอบถามที่สร้างโดยเปิดใช้งาน:<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
ฉันชอบอันนี้ดีที่สุดเพราะsetFirstResultถูกกล่าวถึงในคำตอบนี้จริง ๆ ในขณะที่ที่นี่และที่อื่น ๆ พวกเขาแค่พูดแบบsetMaxResultsนี้และsetMaxResultsโดยไม่ต้องพูดถึงวิธีตั้งค่าออฟเซ็ต
demongolem

19

หากคุณไม่ต้องการใช้setMaxResults()กับQueryวัตถุคุณสามารถเปลี่ยนกลับไปใช้ SQL ปกติได้ตลอดเวลา


37
นั่นไม่ใช่ทั้งหมดที่น่าตื่นเต้น
stevedbrown

8
ฉันไม่พบ HQL ที่น่าตื่นเต้นเช่นกัน ทำไมไม่เขียนมุมมองบนเซิร์ฟเวอร์ DB ของคุณที่ใช้ขีด จำกัด แล้วรับ HQL เพื่อดูมุมมองที่: P
pjp

1
เป็นเพียงหนึ่งในสิ่งเหล่านั้นในขณะที่ SQL นั้นง่ายกว่า HQL มากสำหรับแต่ละแบบสอบถามการสร้างมุมมองและการเขียน SQL ดั้งเดิมมีแนวโน้มที่จะไม่ยอดเยี่ยมสำหรับการปรับโครงสร้างใหม่ ฉันพยายามหลีกเลี่ยงเมื่อทำได้ ปัญหาที่แท้จริงที่แท้จริงคือฉันเขียนแบบสอบถาม MySQL ของฉันผิดและคิดว่า setMaxResults นั้นแปลก มันไม่ใช่
stevedbrown

และถ้าคุณพยายามสลับไปมาระหว่างผู้ขาย DBMS ที่ต่างกันความเจ็บปวดกำลังรอคุณอยู่
Olgun Kaya

5

หากคุณไม่ต้องการใช้ setMaxResults คุณสามารถใช้ Query.scroll แทนรายการและดึงข้อมูลแถวที่คุณต้องการ มีประโยชน์สำหรับการเพจเช่น


ขอบคุณคำตอบที่ยอมรับไม่ได้แก้ปัญหาสำหรับฉันเพราะsetMaxResults()โหลดครั้งแรกทุกระเบียนในหน่วยความจำแล้วสร้างรายการย่อยซึ่งเมื่อมีหลายแสนบันทึกหรือมากกว่าเกิดปัญหาเซิร์ฟเวอร์เพราะหน่วยความจำไม่เพียงพอ อย่างไรก็ตามฉันสามารถเปลี่ยนจากเคียวรีที่พิมพ์ JPA ไปเป็นเคียวรีไฮเบอร์เนตได้QueryImpl hibernateQuery = query.unwrap(QueryImpl.class)จากนั้นฉันก็สามารถใช้scroll()วิธีการตามที่คุณแนะนำ
toongeorges

อย่างน้อยด้วยภาษาถิ่นของออราเคิลสิ่งนี้ไม่เป็นความจริง (ไฮเบอร์เนตใช้คอลัมน์เสมือน ROWNUM) อาจขึ้นอยู่กับไดรเวอร์ ฐานข้อมูลอื่นมีฟังก์ชัน TOP
Lluis Martinez

ข้อความค้นหาของฉันใช้การดึงข้อมูลเข้าร่วม สิ่งนี้ส่งผลให้เกิดคำเตือนไฮเบอร์เนต "firstResult / maxResults ที่ระบุด้วยการดึงข้อมูลคอลเลกชัน ดังนั้นด้วยการดึงข้อมูลเข้าร่วม Hibernate กำลังโหลดคอลเล็กชันทั้งหมดในหน่วยความจำ การดร็อปการเข้าร่วมไม่มีตัวเลือกเนื่องจากเหตุผลด้านประสิทธิภาพ เมื่อฉันใช้ ScrollableResults ฉันมีการควบคุมเพิ่มเติมเกี่ยวกับระเบียนที่โหลดในหน่วยความจำ ฉันไม่สามารถโหลดบันทึกทั้งหมดด้วย ScrollableResults เพียงครั้งเดียวเพราะจะส่งผลให้หน่วยความจำไม่เพียงพอ ฉันกำลังทดลองโหลดหลายหน้าด้วย ScrollableResults ที่แตกต่างกัน หากไม่ได้ผลฉันจะไปกับ SQL
toongeorges

มันแปลกมากฉันไม่เคยเจอแบบนั้นเลย ใช่บางครั้งการใช้ JDBC แบบตรงเป็นวิธีที่จะไปโดยเฉพาะอย่างยิ่งสำหรับกระบวนการขนาดใหญ่ / ชุด
Lluis Martinez

ความสัมพันธ์ @OneToMany ทำให้ฉันมีปัญหา ถ้าอย่างใดฉันสามารถดำเนินการฟังก์ชั่นรวม Oracle LISTAGG ในไฮเบอร์เนตเพื่อเชื่อมต่อค่าหลายค่าเป็นค่าเดียวจากนั้นฉันสามารถวางการเข้าร่วมและแทนที่พวกเขาด้วยแบบสอบถามย่อย
toongeorges

2

คุณสามารถใช้เลขหน้าสำหรับเรื่องนี้ได้อย่างง่ายดาย

    @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)การดึงข้อมูลระเบียนและจากรายการเรียกระเบียนแรก


2

เนื่องจากนี่เป็นคำถามที่พบบ่อยมากฉันจึงเขียน บทความนี้ขึ้นอยู่กับคำตอบนี้

setFirstResultและsetMaxResults Queryวิธีการ

สำหรับ JPA และ Hibernate Queryที่setFirstResultเป็นวิธีการที่เทียบเท่าOFFSETและsetMaxResultsวิธีการที่จะเทียบเท่ากับการ จำกัด :

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

สิ่งที่LimitHandlerเป็นนามธรรม

ไฮเบอร์เนตLimitHandlerกำหนดตรรกะการแบ่งหน้าเฉพาะฐานข้อมูลและตามที่แสดงโดยแผนภาพต่อไปนี้ไฮเบอร์เนตรองรับตัวเลือกการแบ่งหน้าเฉพาะฐานข้อมูลจำนวนมาก:

การใช้งาน <code> LimitHandler </code>

ตอนนี้ขึ้นอยู่กับระบบฐานข้อมูลเชิงสัมพันธ์ที่คุณใช้แบบสอบถาม JPQL ด้านบนจะใช้ไวยากรณ์การแบ่งหน้าที่เหมาะสม

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

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

คำพยากรณ์

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

ข้อดีของการใช้setFirstResultและsetMaxResultsคือไฮเบอร์เนตสามารถสร้างไวยากรณ์การแบ่งหน้าเฉพาะฐานข้อมูลสำหรับฐานข้อมูลเชิงสัมพันธ์ที่รองรับ

และคุณไม่ จำกัด เฉพาะการสืบค้น JPQL เท่านั้น คุณสามารถใช้setFirstResultและsetMaxResultsวิธีที่เจ็ดสำหรับการสืบค้น SQL ดั้งเดิม

การสืบค้น SQL ดั้งเดิม

คุณไม่จำเป็นต้อง hardcode การแบ่งหน้าเฉพาะฐานข้อมูลเมื่อใช้แบบสอบถาม SQL ดั้งเดิม ไฮเบอร์เนตสามารถเพิ่มลงในแบบสอบถามของคุณ

ดังนั้นหากคุณกำลังดำเนินการค้นหา SQL นี้ใน 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();

ไฮเบอร์เนตจะแปลงดังนี้:

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

เจ๋งใช่มั้ย

นอกเหนือจากการแบ่งหน้าตาม SQL

การแบ่งหน้าเป็นสิ่งที่ดีเมื่อคุณสามารถจัดทำดัชนีการกรองและเกณฑ์การเรียงลำดับ หากความต้องการการแบ่งหน้าของคุณบ่งบอกถึงการกรองแบบไดนามิกมันเป็นวิธีที่ดีกว่ามากในการใช้โซลูชันดัชนีกลับหัวเช่น ElasticSearch

ตรวจสอบบทความนี้สำหรับรายละเอียดเพิ่มเติม


1

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

สิ่งนี้ใช้ได้กับฉันโดยไม่ต้องใช้ setmaxResults();

เพียงแค่ให้ค่าสูงสุดในช่วง (ในกรณีนี้ 5) limitโดยไม่ต้องใช้คำว่า : P


7
หืม ... ไม่แน่ใจเกี่ยวกับเรื่องนี้มากเกินไปนี่อาจเป็นอุบัติเหตุและอาจหยุดทำงานในโหมดไฮเบอร์เนตเวอร์ชั่นใหม่ทันที
Konrad 'ktoso' Malawski

4
โปรดอ้างอิงถึงเอกสารอย่างเป็นทางการ
อย่านู Vy

1
สิ่งนี้ไม่ทำงานสำหรับฉันใน Hibernate เวอร์ชัน
5.2.12

1
มันไม่ทำงานไม่รองรับไฮเบอร์เนต 4.x ด้วย
Faiz Akram

0

การสังเกตของฉันคือแม้ว่าคุณจะมีข้อ จำกัด ใน HQL (hibernate 3.x) มันจะทำให้เกิดข้อผิดพลาดในการแยกวิเคราะห์หรือไม่สนใจ (หากคุณมีการสั่งซื้อโดย + desc / asc ก่อนถึงขีด จำกัด มันจะถูกละเว้นหากคุณไม่มี desc / asc ก่อนถึงขีด จำกัด มันจะทำให้เกิดข้อผิดพลาดในการแยกวิเคราะห์)


0

หากสามารถจัดการขีด จำกัด ในโหมดนี้

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

นี่เป็นรหัสที่ง่ายมากในการจัดการขีด จำกัด หรือรายการ


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

คุณต้องเขียนแบบสอบถามพื้นเมืองหมายนี้

@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

คุณสามารถใช้แบบสอบถามด้านล่าง

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

0

ตัวอย่างด้านล่างนี้ใช้เพื่อดำเนินการค้นหาแบบ จำกัด โดยใช้ HQL

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

คุณสามารถรับใบสมัครสาธิตได้ที่ลิงค์นี้


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.