2.1 表级链接

我们已经看到 Hibernate 如何在辅助表中存储集合。现在让我们弄清楚如何组织存储真实实体类的完整表之间的关系。

Hibernate中实体类之间的关系有四种:

  • 一对一_ _
  • 一对多_ _
  • 多对一_ _
  • 多对多_ _

我们将从最简单的选项开始分析——多对一

您已经遇到过 SQL 中表之间的这种关系。它通常是这样的:

ID 姓名 职业 薪水 年龄 加入日期
1个 伊万诺夫伊万 程序员 100000 25 2012-06-30
2个 彼得罗夫彼得 程序员 80000 23 2013-08-12
3个 伊万诺夫谢尔盖 测试员 40000 三十 2014-01-01
4个 拉比诺维奇·莫伊沙 导演 200000 35 2015-05-12
5个 基连科阿纳斯塔西娅 办公室主管 40000 25 2015-10-10
6个 瓦斯卡 1000 3个 2018-11-11

员工表:

该表包含以下列:

  • 标识符整数
  • 名称VARCHAR
  • 职业VARCHAR
  • 薪水整数
  • 年龄整数
  • join_date日期

这就是包含员工任务的任务表的样子:

ID 员工ID 姓名 最后期限
1个 1个 修复一个前端bug 2022-06-01
2个 2个 修复后端的一个bug 2022-06-15
3个 5个 买咖啡 2022-07-01
4个 5个 买咖啡 2022-08-01
5个 5个 买咖啡 2022-09-01
6个 (无效的) 打扫办公室 (无效的)
7 4个 享受生活 (无效的)
8个 6个 享受生活 (无效的)

该表只有 4 列:

  • id – 唯一的任务编号(和表中的行);
  • employee_id – 分配任务的员工表中的员工 ID;
  • 名称——任务的名称和描述;
  • deadline - 任务必须完成的时间。

我们看到任务表中的许多行可以引用员工表中的单个条目。这种表级关系称为多对一

2.2 与Java类级别的关系

除了表级别的通信,您还可以在 Hibernate 中的实体类级别组织通信。这是通过注释完成的@ManyToOne

但首先,让我们创建两个类:EmployeeEmployeeTask


@Entity
@Table(name="employee")
class Employee {
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String name;
 
   @Column(name="occupation")
   public String occupation;
 
   @Column(name="salary")
   public Integer salary;
 
   @Column(name="join_date")
   public Date join;
}

第二类存储员工工作:


@Entity
@Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String description;
 
  @Column(name="employee_id")
   public Integer employeeId;
 
   @Column(name="deadline")
   public Date deadline;
}

这些类都很好,但是它们之间没有关系可以反映 EmployeeTask 类的 employeeId 字段引用 Employee 类的 id 字段这一事实。是时候修复它了

2.3 @ManyToOne注解。

首先,在 Java 中我们习惯于操作对象(和对象引用)而不是它们的 id。所以首先,让我们指向一个 Employee 类型的对象,而不是 EmployeeTask 类中的 employeeId 字段。这是我们的新课程的样子:


@Entity
@Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String description;
 
   @ManyToOne
   @JoinColumn(name = "employee_id")
   public Employee employee;
 
   @Column(name="deadline")
   public Date deadline;
}

在注释 的帮助下@ManyToOne,我们已经指出许多 EmployeeTask 对象可以引用一个 Employee 类型的对象。此外,使用注释 @JoinColumn,我们指出了 Employee 对象的 ID 存储在表的哪一列中。

2.4 请求示例

现在让我们展示一些 Hibernate 如何与这些相关类一起工作的例子。

场景一

让我们编写一个查询来找出已分配给特定用户的所有任务。下面是这个查询在 HQL 中的样子:

from EmployeeTask where employee.name = "Ivan Ivanovich"

您可以简单地通过一个点来引用依赖类的字段。很舒服。但是让我们仍然以 Java 代码的形式编写这个查询:


String hql = "from EmployeeTask where employee.name = :username";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
query.setParameter("username", "Ivan Ivanovich");
List<EmployeeTask> resultLIst = query.list();

情景二

让我们编写一个查询,返回一个有逾期任务的员工列表。如果截止日期已过,则任务已过期。下面是该查询在 SQL 中的样子:


SELECT DISTINCT employee.*
FROM task JOIN employee ON task.employee_id = employee.id
WHERE task.deadline < CURDATE();

DISTINCT之所以使用,是因为可以将许多任务分配给一个用户。

现在让我们用 HQL 编写相同的查询:

select distinct employee from EmployeeTask where deadline < CURDATE();

此查询中的 Employee 是 EmployeeTask 类的一个字段

情况三

将所有未分配的任务分配给主管。SQL 查询将如下所示:


UPDATE task SET employee_id = 4 WHERE employee_id IS NULL

现在让我们用 HQL 编写相同的查询:

update EmployeeTask set employee = :user where employee is null

最后一个查询是最难的。我们需要传递主管的 ID,但是 EmployeeTask 类不包含可以写入 id 的字段,而是包含一个 Employee 字段,我们需要在其中分配对 Employee 类型对象的引用。


Employee director = session.get(Employee.class, 4);
 
String hql = "update EmployeeTask set employee = :user where employee is null";
Query<EmployeeTask> query = session.createQuery(hql, EmployeeTask.class);
query.setParameter("user", director);
query.executeUpdate();