ทำความรู้จักกับ 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 รายการไปยังฐานข้อมูลเพื่อเรียกใช้โค้ดนี้ แม้ว่าจะทำได้ด้วยความคิดเห็นเดียวก็ตาม หากคุณรู้ล่วงหน้าว่าคุณต้องการวัตถุทั้งหมดของชั้นนี้