了解 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 次查詢以執行此代碼,儘管它可以只執行一次。如果您事先知道您將需要該類的所有對象。
GO TO FULL VERSION