了解 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);

那麼問題來了:Hibernate 應該如何在不將所有元素加載到內存的情況下知道該元素是第三個?

為了解決這個問題,建議在評論表中增加一列,用於存儲評論在評論集合中的序號。為此,您還需要一個特殊的註釋 - @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 的主要優點

當我們使用@ManyToMany註釋指定它時,我們看到了 LazyCollectionOption.EXTRA 的最強優勢。讓我們以我們有一個員工和一個任務的舊案例為例,我們可以將許多任務分配給一個用戶。

我們的 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();

因此,如果 Task 類中的 employees 字段具有 LazyCollectionOption.EXTRA 註釋,則(Task 類的)employees 集合和(Employee 類的)task 集合將永遠不會從數據庫中加載

執行此代碼時,只會將一條記錄插入到 employee_task 服務表中,您還記得,它看起來像這樣:

員工任務表
員工ID 任務編號
1個 1個
2個 2個
5個 3個
5個 4個
5個 5個
4個 7
6個 8個
4個 101

添加的行以綠色突出顯示。要添加這一行,您不需要從數據庫加載集合——Hibernate 不需要它。這正是 LazyCollectionOption.EXTRA 大大加快數據庫工作的情況。

N+1問題

但是,當然,這種模式也有缺點。例如,LazyCollectionOption.EXTRA 註釋生成N+1 Problem

如果您決定查看所有用戶的評論:

User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
for (Comment comment : comments) {
    System.out.println(comment);
}

然後 Hibernate 將對每個 Comment 對象的單獨請求執行。還有一個額外的查詢來獲取所有評論的數量。這會顯著降低代碼速度。

如果您的用戶有 1000 條評論,那麼 Hibernate 將對數據庫進行 1001 次查詢以執行此代碼,儘管它可以只執行一次。如果您事先知道您將需要該類的所有對象。