คำอธิบายของปัญหา

ดังที่เรากล่าวไว้ข้างต้น คำอธิบายประกอบLazyCollectionOption.EXTRAมีปัญหา - จะดำเนินการร้องขอแยกต่างหากไปยังฐานข้อมูลสำหรับแต่ละวัตถุ เราจำเป็นต้องอธิบายให้ Hibernate ทราบว่าเราต้องการให้โหลดวัตถุลูกทั้งหมดสำหรับวัตถุหลักของเราทันที

นักพัฒนาของ Hibernate ได้คิดวิธีแก้ปัญหานี้ขึ้นมา ซึ่งก็คือ ตัวดำเนินการ ดึงข้อมูลร่วมใน HQL

ตัวอย่างแบบสอบถาม HQL:

select distinct task from Task t left join fetch t.employee order by t.deadline

ในข้อความค้นหานี้ ทุกอย่างเรียบง่ายและซับซ้อนในเวลาเดียวกัน ลองสร้างทีละชิ้น

ตัวเลือกที่ 1

เราต้องการดาวน์โหลดวัตถุทั้งหมดงานเรียงตามกำหนดเวลา นี่คือลักษณะของคำขอนั้น:

select task from Task t order by t.deadline

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

ตัวเลือก 2

บังคับให้ Hibernate โหลดวัตถุย่อยสำหรับวัตถุงาน.

select task from Task t join fetch t.employee order by t.deadline

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

แต่เราต้องการคำสั่งjoinเพื่อให้สมบูรณ์ ด้วย คำสั่งfetchเพื่อรับjoin fetch นี่คือวิธีที่เราบอกไฮเบอร์เนตว่าวัตถุในคอลเลกชัน Task.employee จำเป็นต้องโหลดจากฐานข้อมูลเมื่อคำขอของเราถูกดำเนินการ

ตัวเลือก 3

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

ดังนั้นเราจึงจำเป็นต้องเพิ่มการรวม ของเราด้วย ตัวดำเนินการด้านซ้ายและเปลี่ยนเป็นการรวมด้านซ้าย ตัวอย่าง:

select task from Task t left join fetch t.employee order by t.deadline

ตัวเลือก 4

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

ดังนั้นคุณต้องเพิ่มคำหลักเฉพาะหลังจากคำที่เลือกเพื่อกำจัดวัตถุงานที่ซ้ำกัน

select distinct task from Task t left join fetch t.employee order by t.deadline

นี่คือวิธีใน 4 ขั้นตอนที่เราได้รับคำขอที่เราเริ่มต้น โค้ด Java จะดูค่อนข้างคาดหวัง:

String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
return query.list();

เข้าร่วม FETCH ข้อ จำกัด

ไม่มีใครสมบูรณ์แบบ. เข้าร่วมคำสั่ง FETCH ด้วย มีข้อจำกัดค่อนข้างมาก และอันแรกใช้ เมธอด setMaxResults()และsetFirstResult( )

สำหรับคำสั่ง JOIN FETCH การไฮเบอร์เนตของเราจะสร้างแบบสอบถามที่ซับซ้อนมาก ซึ่งเรารวมสามตารางเป็นหนึ่งเดียว: ลูกจ้าง งาน และลูกจ้าง_งาน อันที่จริง นี่ไม่ใช่คำขอสำหรับพนักงานหรืองาน แต่เป็นคำขอสำหรับคู่งานของพนักงานที่รู้จักทั้งหมด

และ SQL สามารถใช้คำสั่ง LIMIT และ OFFSET กับการค้นหาคู่งานของพนักงานได้ ในเวลาเดียวกัน การสืบค้น HQL นั้นตามมาอย่างชัดเจนว่าเราต้องการได้งาน (Task) อย่างแน่นอน และถ้าเราแจกจ่ายพารามิเตอร์ FirstResult และ MaxResult อีกครั้ง ก็ควรอ้างอิงถึงวัตถุงานโดยเฉพาะ

หากคุณเขียนโค้ดดังนี้:

String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
       	    query.setFirstResult(0);
        	   query.setMaxResults(1);
return query.list();

จากนั้นไฮเบอร์เนตจะไม่สามารถแปลง FirstResult และ MaxResult เป็นพารามิเตอร์ OFFSET และ LIMIT ของแบบสอบถาม SQL ได้อย่างถูกต้อง

มันจะทำสามสิ่งแทน:

  • แบบสอบถาม SQL จะเลือกข้อมูลทั้งหมดจากตารางโดยทั่วไปและส่งกลับไปยังไฮเบอร์เนต
  • ไฮเบอร์เนตจะเลือกบันทึกที่จำเป็นในหน่วยความจำและส่งกลับมาให้คุณ
  • Hibernate จะออกคำเตือน

คำเตือนจะเป็นดังนี้:

WARN [org.hibernate.hql.internal.ast.QueryTranslatorImpl] HHH000104: 
firstResult/maxResults specified with collection fetch; applying in memory!