คำอธิบายของปัญหา
ดังที่เรากล่าวไว้ข้างต้น คำอธิบายประกอบ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!
GO TO FULL VERSION