2.1 Łączenie na poziomie tabeli
Widzieliśmy, jak Hibernate przechowuje kolekcje w tabelach pomocniczych. Teraz zastanówmy się, jak zorganizować relacje między pełnoprawnymi tabelami, które przechowują prawdziwe klasy Entity.
Istnieją cztery typy relacji między klasami jednostek w Hibernate:
- jeden do jednego
- jeden do wielu
- wiele do jednego
- wiele do wielu
A analizę zaczniemy od najprostszej opcji – wiele do jednego .
Spotkałeś się już z taką zależnością między tabelami w SQL. Oto jak zwykle wygląda:
ID | nazwa | zawód | wynagrodzenie | wiek | data_dołączenia |
---|---|---|---|---|---|
1 | Iwanow Iwan | Programista | 100000 | 25 | 2012-06-30 |
2 | Pietrow Pietr | Programista | 80000 | 23 | 2013-08-12 |
3 | Iwanow Siergiej | Próbnik | 40000 | trzydzieści | 2014-01-01 |
4 | Rabinowicz Mojsza | Dyrektor | 200000 | 35 | 2015-05-12 |
5 | Kirienko Anastazja | Kierownik biura | 40000 | 25 | 2015-10-10 |
6 | Vaska | Kot | 1000 | 3 | 2018-11-11 |
stół pracowniczy:
Ta tabela ma następujące kolumny:
- identyfikator INT
- imię VARCHAR
- zawód VARCHAR
- wynagrodzenie INT
- wiek INT
- data_dołączenia DATA
A tak wygląda tabela zadań , która zawiera zadania dla pracowników:
ID | dowód pracownika | nazwa | termin ostateczny |
---|---|---|---|
1 | 1 | Napraw błąd w interfejsie użytkownika | 2022-06-01 |
2 | 2 | Napraw błąd w backendzie | 2022-06-15 |
3 | 5 | Kup kawę | 2022-07-01 |
4 | 5 | Kup kawę | 2022-08-01 |
5 | 5 | Kup kawę | 2022-09-01 |
6 | (ZERO) | Posprzątaj biuro | (ZERO) |
7 | 4 | Ciesz się życiem | (ZERO) |
8 | 6 | Ciesz się życiem | (ZERO) |
Ta tabela ma tylko 4 kolumny:
- id – unikalny numer zadania (i wierszy w tabeli);
- id_pracownika – identyfikator pracownika z tabeli pracowników, do którego zadanie jest przypisane;
- nazwa – nazwa i opis zadania;
- termin – czas, w jakim zadanie musi zostać wykonane.
Widzimy, że wiele wierszy w tabeli zadań może odnosić się do pojedynczego wpisu w tabeli pracowników. Taka relacja na poziomie tabeli nazywa się wiele do jednego.
2.2 Związek z poziomem klasy Java
Poza komunikacją na poziomie tabeli, można również zorganizować komunikację na poziomie klas Entity w Hibernate. Odbywa się to za pomocą adnotacji @ManyToOne
.
Ale najpierw utwórzmy dwie klasy: Employee i 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;
}
I druga klasa do przechowywania zadań pracowników:
@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;
}
Z tymi klasami wszystko jest w porządku, ale nie ma między nimi związku, który odzwierciedlałby fakt, że pole EmployeeId klasy EmployeeTask odnosi się do pola id klasy Employee. Czas to naprawić
2.3 Adnotacja @ManyToOne.
Po pierwsze, w Javie jesteśmy przyzwyczajeni do operowania na obiektach (i referencjach do obiektów), a nie na ich id. Po pierwsze, zamiast pola EmployeeId w klasie EmployeeTask wskażmy po prostu obiekt typu Employee. Oto jak będzie wyglądać nasza nowa klasa:
@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;
}
Za pomocą adnotacji @ManyToOne
wskazaliśmy, że wiele obiektów EmployeeTask może odnosić się do jednego obiektu typu Pracownik. Również za pomocą adnotacji @JoinColumn
wskazaliśmy w której kolumnie naszej tabeli przechowywane jest id obiektu Employee.
2.4 Poproś o przykłady
A teraz pokażmy kilka przykładów, jak Hibernate może współpracować z takimi pokrewnymi klasami.
Scenariusz pierwszy
Napiszmy zapytanie, aby znaleźć wszystkie zadania, które zostały przypisane do określonego użytkownika. Oto jak wyglądałoby to zapytanie w HQL:
from EmployeeTask where employee.name = "Ivan Ivanovich"
Możesz po prostu odwołać się do pól klas zależnych za pomocą kropki. To jest bardzo wygodne. Ale nadal napiszmy to zapytanie w postaci kodu 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();
Scenariusz drugi
Napiszmy zapytanie, które zwróci listę pracowników, którzy mają zaległe zadania. Zadanie jest opóźnione, jeśli jego termin przypada w przeszłości. Oto jak wyglądałoby to zapytanie w SQL:
SELECT DISTINCT employee.*
FROM task JOIN employee ON task.employee_id = employee.id
WHERE task.deadline < CURDATE();
DISTINCT
jest używany, ponieważ do jednego użytkownika można przypisać wiele zadań.
A teraz napiszmy to samo zapytanie w HQL:
select distinct employee from EmployeeTask where deadline < CURDATE();
Pracownik w tym zapytaniu jest polem klasy EmployeeTask
Sytuacja trzecia
Przydziel wszystkie nieprzypisane zadania dyrektorowi. Zapytanie SQL będzie wyglądać następująco:
UPDATE task SET employee_id = 4 WHERE employee_id IS NULL
A teraz napiszmy to samo zapytanie w HQL:
update EmployeeTask set employee = :user where employee is null
Ostatnie zapytanie jest najtrudniejsze. Musimy przekazać identyfikator dyrektora, ale klasa EmployeeTask nie zawiera pola, w którym możemy wpisać id, zamiast tego zawiera pole Pracownik, w którym musimy przypisać referencję do obiektu typu Pracownik.
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();