@wielu do wielu

Dostępny

Stół serwisowy

Teraz spójrzmy na inny częsty przypadek — wiele do wielu. Wyobraźmy sobie, że mamy relację wiele do wielu między zadaniami a pracownikami :

  • Jeden pracownik w tabeli pracowników może wykonywać wiele zadań z tabeli zadań.
  • Jedno zadanie w tabeli zadań można przypisać kilku pracownikom.

Ta relacja między jednostkami nazywana jest wiele-do-wielu. A żeby to zaimplementować na poziomie SQL potrzebujemy dodatkowej tabeli usług. Nazwijmy to na przykład pracownik_zadanie.

Tabela pracownik_zadanie będzie zawierała tylko dwie kolumny:

  • dowód pracownika
  • identyfikator_zadania

Za każdym razem, gdy przypiszemy określone zadanie konkretnemu użytkownikowi, do tej tabeli zostanie dodany nowy wiersz. Przykład:

dowód pracownika identyfikator_zadania
1 1
1 2
2 3

Cóż, tabela zadań powinna utracić swoją kolumnę id_pracownika . Ma to sens tylko wtedy, gdy zadanie można przypisać tylko jednemu pracownikowi. Jeśli zadanie można przypisać kilku pracownikom, to informacja ta musi być przechowywana w tabeli usługi pracownik_zadanie .

Relacja na poziomie tabeli

Oto jak będą wyglądać nasze nowe stoły:

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

Tabela pracowników ( bez zmian ) :

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ń , zgubiona kolumna id_pracownika (zaznaczona na czerwono):

ID identyfikator_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 teraz tylko 3 kolumny:

  • id - unikalny numer zadania (i wierszy w tabeli)
  • id_pracownika - (usunięto)
  • name - nazwa i opis zadania
  • termin – czas, do którego zadanie musi zostać wykonane

Mamy również tabelę usługi Employee_task , w której dane o identyfikatorze_pracownika zostały przeniesione z tabeli zadań:

dowód pracownika identyfikator_zadania
1 1
2 2
5 3
5 4
5 5
(ZERO) 6
4 7
6 8

Celowo tymczasowo zapisałem usuniętą kolumnę w tabeli zadań, abyś mógł zobaczyć, że dane z niej zostały przeniesione do tabeli pracownik_zadanie.

Kolejnym ważnym punktem jest czerwona linia „(NULL) 6” w tabeli pracownik_zadanie. Zaznaczyłem go na czerwono, ponieważ nie będzie go w tabeli Employee_task .

Jeśli zadanie 7 jest przypisane do użytkownika 4, to w tabeli pracownik_zadanie powinien znajdować się wiersz (4, 7).

Jeśli zadanie 6 nie zostanie nikomu przydzielone, po prostu nie będzie dla niego żadnego rekordu w tabeli pracownik_zadanie. Oto jak będą wyglądać ostateczne wersje tych tabel:

tabela zadań :

ID nazwa termin ostateczny
1 Napraw błąd w interfejsie użytkownika 2022-06-01
2 Napraw błąd w backendzie 2022-06-15
3 Kup kawę 2022-07-01
4 Kup kawę 2022-08-01
5 Kup kawę 2022-09-01
6 Posprzątaj biuro (ZERO)
7 Ciesz się życiem (ZERO)
8 Ciesz się życiem (ZERO)

tabela_zadanie_pracownika:

dowód pracownika identyfikator_zadania
1 1
2 2
5 3
5 4
5 5
4 7
6 8

Komunikacja na poziomie klasy Java

Ale z komunikacją na poziomie klas Entity mamy kompletny porządek. Zacznijmy od dobrych wiadomości.

Po pierwsze, Hibernate ma specjalną adnotację @ManyToMany , która pozwala dobrze opisać przypadek relacji tabel wiele-do-wielu.

Po drugie, wciąż wystarczą nam dwie klasy Entity. Nie potrzebujemy klasy dla stołu serwisowego.

Oto jak będą wyglądać nasze zajęcia. Klasa Pracownik w oryginalnej formie:

@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;
}

I klasa EmployeeTask w oryginalnej postaci:

@Entity
@Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;

   @Column(name="name")
   public String description;

   @Column(name="deadline")
   public Date deadline;
}

Adnotacja @ManyToMany

W przykładach pominę istniejące pola, ale dodam nowe. Oto jak będą wyglądać. Klasa pracownika :

@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>();

}

I klasa 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>();

}

Wydaje się, że wszystko jest skomplikowane, ale w rzeczywistości wszystko jest proste.

Po pierwsze, używa adnotacji @JoinTable (nie mylić z @JoinColumn), która opisuje tabelę usług Employee_task.

Po drugie, opisuje, że kolumna id_zadania w tabeli pracownik_zadanie odwołuje się do kolumny id w tabeli zadań.

Po trzecie, mówi, że kolumna id_pracownika w tabeli pracownik_zadanie odnosi się do kolumny id w tabeli pracownik.

Tak naprawdę za pomocą adnotacji opisaliśmy, jakie dane są zawarte w tabeli pracownik_zadanie i jak Hibernate powinien je interpretować.

Ale teraz możemy bardzo łatwo dodać (i usunąć) zadanie dla dowolnego pracownika. A także dodaj dowolnego wykonawcę do dowolnego zadania.

Poproś o przykłady

Napiszmy kilka interesujących zapytań, aby lepiej zrozumieć, jak działają te pola ManyToMany. I działają dokładnie tak, jak oczekiwano.

Po pierwsze, nasz stary kod będzie działał bez zmian, ponieważ wcześniej dyrektor miał pole zadań:

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

Po drugie, jeśli chcemy przypisać do jakiegoś zadania innego wykonawcę, to jeszcze prościej jest to zrobić:

Employee director = session.find(Employee.class, 4);
EmployeeTask task = session.find(EmployeeTask.class, 101);
task.employees.add(director);

session.update(task);
session.flush();

Ważny! W wyniku realizacji tego zlecenia nie tylko zadanie będzie miało wykonawcę-kierownika, ale także dyrektor będzie miał zadanie nr 101.

Po pierwsze, fakt o relacji między dyrektorem a zadaniem w tabeli pracownik_zadanie zostanie zapisany jako ciąg znaków: (4,101).

Po drugie, pola oznaczone adnotacjami @ManyToMany są obiektami proxy, a kiedy uzyskuje się do nich dostęp, zawsze wykonywane jest zapytanie do bazy danych.

Jeśli więc dodasz zadanie do pracownika i zapiszesz informacje o pracowniku do bazy danych, to zadanie będzie miało nowego wykonawcę na liście wykonawców.

Komentarze
  • Popularne
  • Najnowsze
  • Najstarsze
Musisz się zalogować, aby dodać komentarz
Ta strona nie ma jeszcze żadnych komentarzy