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 :

class Employee {
   public Integer id;
   public String name;
   public String occupation;
   public Integer salary;
   public Date join;

And a second class to store employee jobs:

class EmployeeTask {
   public Integer id;
   public String description;
   public Integer employeeId;
   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:

class EmployeeTask {
   public Integer id;
   public String description;
   @JoinColumn(name = "employee_id")
   public Employee employee;
   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:

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

DISTINCTis 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);

Module 4. Working with databases, level 13, lesson 1
Books again