ทำความรู้จักกับ LazyCollectionOption.EXTRA
แต่ที่น่าสนใจที่สุดคือค่า LazyCollectionOption.EXTRA หากคุณระบุเป็นค่าของ คำอธิบายประกอบ @LazyCollectionจากนั้น Hibernate จะชะลอการโหลดองค์ประกอบของคอลเลกชันให้นานที่สุด
หากคุณพยายามรับจำนวนองค์ประกอบในคอลเล็กชัน:
User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
int count = commetns.size();
จากนั้นสำหรับโค้ดทั้งหมดนี้ Hibernate จะดำเนินการค้นหาเพียงรายการเดียว:
SELECT COUNT(id) FROM comment WHERE user_id = 1;
อย่างไรก็ตาม หากคุณต้องการรับหนึ่งความคิดเห็นจากคอลเล็กชัน ตัวอย่างเช่น หมายเลข 3:
User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
Comment comment = commetns.get(3);
จากนั้นคำถามก็เกิดขึ้น: ไฮเบอร์เนตควรรู้ได้อย่างไรว่าองค์ประกอบนั้นเป็นองค์ประกอบที่สามโดยไม่ต้องโหลดองค์ประกอบทั้งหมดลงในหน่วยความจำ
เพื่อแก้ปัญหานี้ ขอเสนอให้สร้างคอลัมน์เพิ่มเติมในตารางความคิดเห็น ซึ่งจะจัดเก็บหมายเลขลำดับของความคิดเห็นในการรวบรวมความคิดเห็น และสำหรับสิ่งนี้ คุณต้องมีคำอธิบายประกอบพิเศษ- @OrderColumn
นี่คือลักษณะของโซลูชันดังกล่าว:
@Entity
@Table(name=”user”)
class User {
@Column(name=”id”)
public Integer id;
@OneToMany(cascade = CascadeType.ALL)
@LazyCollection(LazyCollectionOption.EXTRA)
@OrderColumn(name = "order_id")
public List<Comment> comments;
}
ข้อได้เปรียบหลักของ LazyCollectionOption.EXTRA
เราเห็นข้อได้ เปรียบที่แข็งแกร่งที่สุดของ LazyCollectionOption.EXTRA เมื่อเราระบุด้วย คำอธิบายประกอบ @ManyToMany ลองมาดูกรณีเก่าของเราที่เรามีพนักงาน หนึ่งงาน และเราสามารถมอบหมายงานมากมายให้กับผู้ใช้คนเดียว
คลาส Java ของเรามีลักษณะดังนี้:
ระดับพนักงาน :
@Entity
@Table(name=”employee”)
class Employee {
@Column(name=”id”)
public Integer id;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="employee_task",
joinColumns= @JoinColumn(name="employee_id", referencedColumnName="id"),
inverseJoinColumns= @JoinColumn(name="task_id", referencedColumnName="id") )
@LazyCollection(LazyCollectionOption.EXTRA)
private Set<EmployeeTask> tasks = new HashSet<EmployeeTask>();
}
และคลาส EmployeeTask :
@Entity
@Table(name=”task”)
class EmployeeTask {
@Column(name=”id”)
public Integer id;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="employee_task",
joinColumns= @JoinColumn(name="task_id", referencedColumnName="id"),
inverseJoinColumns= @JoinColumn(name=" employee_id", referencedColumnName="id") )
@LazyCollection(LazyCollectionOption.EXTRA)
private Set<Employee> employees = new HashSet<Employee>();
}
และเพื่อเพิ่มงานให้กับผู้กำกับ คุณต้องเขียนโค้ดดังนี้:
Employee director = session.find(Employee.class, 4);
EmployeeTask task = session.find(EmployeeTask.class, 101);
task.employees.add(director);
session.update(task);
session.flush();
ดังนั้น หากฟิลด์พนักงานในคลาสงานมีคำอธิบายประกอบ LazyCollectionOption.EXTRA คอลเลกชันพนักงาน (ของคลาสงาน) และคอลเลกชันงาน (ของคลาสพนักงาน) จะไม่ถูกโหลดจากฐานข้อมูลเลย
เมื่อรหัสนี้ถูกดำเนินการ จะมีเพียงบันทึกเดียวเท่านั้นที่จะถูกแทรกลงในตารางบริการของ Employee_task ซึ่งตามที่คุณจำได้ จะมีลักษณะดังนี้:
ตารางพนักงาน_task :รหัสพนักงาน | task_id |
---|---|
1 | 1 |
2 | 2 |
5 | 3 |
5 | 4 |
5 | 5 |
4 | 7 |
6 | 8 |
4 | 101 |
บรรทัดที่เพิ่มจะถูกเน้นด้วยสีเขียว ในการเพิ่มบรรทัดนี้ คุณไม่จำเป็นต้องโหลดคอลเล็กชันจากฐานข้อมูล - ไฮเบอร์เนตจะทำโดยไม่ต้องใช้ นี่เป็นกรณีที่ LazyCollectionOption.EXTRA เพิ่มความเร็วในการทำงานกับฐานข้อมูลอย่างมาก
ปัญหา N+1
แต่แน่นอนว่าโหมดนี้ก็มีข้อเสียเช่นกัน ตัวอย่างเช่น คำอธิบายประกอบ LazyCollectionOption.EXTRA สร้างปัญหาN+1
หากคุณตัดสินใจที่จะอ่านความคิดเห็นทั้งหมดของผู้ใช้:
User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
for (Comment comment : comments) {
System.out.println(comment);
}
จากนั้น Hibernate จะดำเนินการตามคำขอแยกต่างหากสำหรับแต่ละออบเจกต์ความคิดเห็น และอีกหนึ่งคำถามเพิ่มเติมเพื่อรับจำนวนความคิดเห็นทั้งหมด สิ่งนี้อาจทำให้โค้ดช้าลงอย่างมาก
หากผู้ใช้ของคุณมีความคิดเห็น 1,000 รายการ Hibernate จะทำการสืบค้น 1,001 รายการไปยังฐานข้อมูลเพื่อเรียกใช้โค้ดนี้ แม้ว่าจะทำได้ด้วยความคิดเห็นเดียวก็ตาม หากคุณรู้ล่วงหน้าว่าคุณต้องการวัตถุทั้งหมดของชั้นนี้
GO TO FULL VERSION