Сервизна маса
Сега нека разгледаме друг често срещан случай - много към много. Нека си представим, че имаме връзка много към много между задачи и служители :
- Един служител в tableта на служителите може да изпълнява много задачи от tableта със задачи.
- Една задача в tableта със задачи може да бъде възложена на няколко служители.
Тази връзка между обектите се нарича много към много. И за да го реализираме на ниво SQL, имаме нужда от допълнителна сервизна table. Нека го наречем, например, employee_task.
Таблицата employee_task ще съдържа само две колони:
- ИД на служител
- task_id
Всеки път, когато възложим конкретна задача на конкретен потребител, към тази table ще бъде добавен нов ред. Пример:
ИД на служител | task_id |
---|---|
1 | 1 |
1 | 2 |
2 | 3 |
Е, tableта на задачите трябва да загуби своята колона employee_id . Има смисъл само ако задачата може да бъде възложена само на един служител. Ако задачата може да бъде възложена на няколко служители, тогава тази информация трябва да се съхранява в служебната table employee_task .
Връзка на ниво table
Ето How ще изглеждат новите ни маси:
document за самоличност | име | професия | заплата | възраст | дата на присъединяване |
---|---|---|---|---|---|
1 | Ivanов Ivan | Програмист | 100 000 | 25 | 2012-06-30 |
2 | Peterов Петър | Програмист | 80 000 | 23 | 2013-08-12 |
3 | Ivanов Сергей | Тестер | 40 000 | тридесет | 2014-01-01 |
4 | Рабинович Мойша | Директор | 200 000 | 35 | 2015-05-12 |
5 | Кириенко Анастасия | Офис мениджър | 40 000 | 25 | 2015-10-10 |
6 | Васка | котка | 1000 | 3 | 2018-11-11 |
Таблица на служителите ( непроменена ) :
Тази table има следните колони:
- id INT
- име VARCHAR
- професия VARCHAR
- заплата INT
- възраст INT
- дата на присъединяване ДАТА
Ето How изглежда tableта на задачите , загубила колоната employee_id (маркирана в червено):
document за самоличност | Emploee_id | име | краен срок |
---|---|---|---|
1 | 1 | Коригиране на грешка във фронтенда | 2022-06-01 |
2 | 2 | Коригиране на грешка в бекенда | 2022-06-15 |
3 | 5 | Купи кафе | 2022-07-01 |
4 | 5 | Купи кафе | 2022-08-01 |
5 | 5 | Купи кафе | 2022-09-01 |
6 | (НУЛА) | Почистете офиса | (НУЛА) |
7 | 4 | Наслаждавай се на живота | (НУЛА) |
8 | 6 | Наслаждавай се на живота | (НУЛА) |
Тази table вече има само 3 колони:
- id - уникален номер на задача (и редове в tableта)
- employee_id - (премахнато)
- име - името и описанието на задачата
- краен срок - времето, до което задачата трябва да бъде изпълнена
Имаме и сервизната table employee_task , където данните за employee_id са мигрирали от tableта на задачите:
ИД на служител | task_id |
---|---|
1 | 1 |
2 | 2 |
5 | 3 |
5 | 4 |
5 | 5 |
(НУЛА) | 6 |
4 | 7 |
6 | 8 |
Нарочно запазих временно изтритата колона в tableта със задачи, за да видите, че данните от нея са се преместor в tableта employee_task.
Друг важен момент е червеният ред "(NULL) 6" в tableта employee_task. Маркирах го в червено, защото няма да бъде в tableта employee_task .
Ако задача 7 е присвоена на потребител 4, тогава трябва да има ред (4, 7) в tableта employee_task.
Ако задача 6 не е възложена на никого, тогава просто няма да има запис за нея в tableта employee_task. Ето How ще изглеждат окончателните версии на тези таблици:
table със задачи :
document за самоличност | име | краен срок |
---|---|---|
1 | Коригиране на грешка във фронтенда | 2022-06-01 |
2 | Коригиране на грешка в бекенда | 2022-06-15 |
3 | Купи кафе | 2022-07-01 |
4 | Купи кафе | 2022-08-01 |
5 | Купи кафе | 2022-09-01 |
6 | Почистете офиса | (НУЛА) |
7 | Наслаждавай се на живота | (НУЛА) |
8 | Наслаждавай се на живота | (НУЛА) |
table за_задача на служител:
ИД на служител | task_id |
---|---|
1 | 1 |
2 | 2 |
5 | 3 |
5 | 4 |
5 | 5 |
4 | 7 |
6 | 8 |
Комуникация на ниво Java клас
Но с комуникацията на ниво Entity-класове имаме пълен ред. Да започнем с добрата новина.
Първо, Hibernate има специална анотация @ManyToMany , която ви позволява да опишете добре случая на връзка на table много към много.
Второ, два класа Entity все още са ни достатъчни. Нямаме нужда от клас за сервизната маса.
Ето How ще изглеждат нашите класове. Класът Employee в оригиналната му форма:
@Entity
@Table(name="user")
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;
}
И класът EmployeeTask в оригиналната му форма:
@Entity
@Table(name="task")
class EmployeeTask {
@Column(name="id")
public Integer id;
@Column(name="name")
public String description;
@Column(name="deadline")
public Date deadline;
}
@ManyToMany анотация
Ще пропусна съществуващите полета в примерите, но ще добавя нови. Ето How ще изглеждат. Клас служител :
@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") )
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") )
private Set<Employee> employees = new HashSet<Employee>();
}
Изглежда, че всичко е сложно, но всъщност всичко е просто.
Първо, той използва анотацията @JoinTable (да не се бърка с @JoinColumn), която описва служебната table employee_task.
Второ, той описва, че колоната task_id на tableта employee_task препраща към колоната id на tableта със задачи.
Трето, той казва, че колоната employee_id на tableта employee_task препраща към колоната id на tableта employee.
Всъщност с помощта на анотации описахме Howви данни се съдържат в tableта employee_task и How Hibernate трябва да ги интерпретира.
Но сега можем много лесно да добавим (и изтрием) задача към всеки служител. И също така добавете всеки изпълнител към всяка задача.
Поискайте примери
Нека напишем няколко интересни заявки, за да разберем по-добре How работят тези ManyToMany полета. И те работят точно Howто се очаква.
Първо, старият ни code ще работи без промени, тъй като директорът имаше поле за задачи преди:
EmployeeTask task1 = new EmployeeTask();
task1.description = "Do Something Important";
session.persist(task1);
EmployeeTask task2 = new EmployeeTask();
task2.description = "Nothing to do";
session.persist(task2);
session.flush();
Employee director = session.find(Employee.class, 4);
director.tasks.add(task1);
director.tasks.add(task2);
session.update(director);
session.flush();
Второ, ако искаме да възложим на друг изпълнител няHowва задача, тогава е още по-лесно да направим това:
Employee director = session.find(Employee.class, 4);
EmployeeTask task = session.find(EmployeeTask.class, 101);
task.employees.add(director);
session.update(task);
session.flush();
важно! В резултат на изпълнение на тази заявка не само задачата ще има изпълнител-директор, но и директорът ще има задача № 101.
Първо, фактът за връзката между директора и задачата в tableта employee_task ще бъде съхранен като низ: (4,101).
Второ, полетата, маркирани с анотации @ManyToMany , са прокси обекти и когато се осъществи достъп до тях, винаги се изпълнява заявка към базата данни.
Така че, ако добавите задача към служител и запазите информация за служителя в базата данни, след това задачата ще има нов изпълнител в списъка с изпълнители.