2.1 Linking at the table level
We have seen how Hibernate stores collections in auxiliary tables. Now let's figure out how to organize relationships between full-fledged tables that store real Entity classes.
There are four types of relationships between Entity classes in Hibernate:
- one -to- one
- one -to- many
- many -to- one
- many -to- many
And we will start the analysis with the simplest option - many -to- one .
You have already come across such a relationship between tables in SQL. Here's what it usually looks like:
id | name | occupation | salary | age | join_date |
---|---|---|---|---|---|
1 | Ivanov Ivan | Programmer | 100000 | 25 | 2012-06-30 |
2 | Petrov Petr | Programmer | 80000 | 23 | 2013-08-12 |
3 | Ivanov Sergey | Tester | 40000 | thirty | 2014-01-01 |
4 | Rabinovich Moisha | Director | 200000 | 35 | 2015-05-12 |
5 | Kirienko Anastasia | Office Manager | 40000 | 25 | 2015-10-10 |
6 | Vaska | Cat | 1000 | 3 | 2018-11-11 |
employee table:
This table has the following columns:
- id INT
- name VARCHAR
- occupation VARCHAR
- salary INT
- age INT
- join_date DATE
And this is how the task table , which contains tasks for employees, looks like:
id | employee_id | name | deadline |
---|---|---|---|
1 | 1 | Fix a bug on the frontend | 2022-06-01 |
2 | 2 | Fix a bug on the backend | 2022-06-15 |
3 | 5 | Buy coffee | 2022-07-01 |
4 | 5 | Buy coffee | 2022-08-01 |
5 | 5 | Buy coffee | 2022-09-01 |
6 | (NULL) | Clean up the office | (NULL) |
7 | 4 | Enjoy life | (NULL) |
8 | 6 | Enjoy life | (NULL) |
This table has only 4 columns:
- id – unique task number (and rows in the table);
- employee_id – employee ID from the employee table to which the task is assigned;
- name – name and description of the task;
- deadline - the time by which the task must be completed.
We see that many rows in the task table can refer to a single entry in the employee table. Such a table-level relationship is called many-to -one.
2.2 Relationship to the Java class level
In addition to communication at the table level, you can also organize communication at the level of Entity classes in Hibernate. This is done with an annotation @ManyToOne
.
But first, let's just create two classes: Employee and EmployeeTask :
@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;
}
And a second class to store employee jobs:
@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;
}
All is well with these classes, but there is no relationship between them that would reflect the fact that the employeeId field of the EmployeeTask class refers to the id field of the Employee class. It's time to fix it
2.3 @ManyToOne annotation.
First, in Java we are accustomed to operating on objects (and object references) rather than their id. So first of all, instead of the employeeId field in the EmployeeTask class, let's just point to an object of type Employee. Here's what our new class will look like:
@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;
}
With the help of the annotation @ManyToOne
, we have indicated that many EmployeeTask objects can refer to one object of type Employee. Also, using the annotation @JoinColumn
, we indicated in which column of our table the id of the Employee object is stored.
2.4 Request examples
And now let's show some examples of how Hibernate can work with such related classes.
Scenario one
Let's write a query to find out all the tasks that have been assigned to a specific user. Here is how this query would look like in HQL:
from EmployeeTask where employee.name = "Ivan Ivanovich"
You can simply refer to fields of dependent classes through a dot. It is very comfortable. But let's still write this query in the form of Java code:
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();
Scenario two
Let's write a query that returns a list of employees who have overdue tasks. A task is overdue if its deadline is in the past. Here's what that query would look like in SQL:
SELECT DISTINCT employee.*
FROM task JOIN employee ON task.employee_id = employee.id
WHERE task.deadline < CURDATE();
DISTINCT
is used because there can be many tasks assigned to one user.
And now let's write the same query in HQL:
select distinct employee from EmployeeTask where deadline < CURDATE();
Employee in this query is a field of the EmployeeTask class
Situation three
Assign all unassigned tasks to the director. The SQL query will look like this:
UPDATE task SET employee_id = 4 WHERE employee_id IS NULL
And now let's write the same query in HQL:
update EmployeeTask set employee = :user where employee is null
The last query is the hardest one. We need to pass the ID of the director, but the EmployeeTask class does not contain a field where we can write an id, instead it contains an Employee field where we need to assign a reference to an object of type 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();