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