Servicebord

Låt oss nu titta på ett annat vanligt fall - många-till-många. Låt oss föreställa oss att vi har ett många-till-många- förhållande mellan uppgifter och anställda :

  • En anställd i medarbetartabellen kan göra många uppgifter från uppgiftstabellen.
  • En uppgift i uppgiftstabellen kan tilldelas flera anställda.

Denna relation mellan enheter kallas många-till-många. Och för att implementera det på SQL-nivå behöver vi en extra servicetabell. Låt oss kalla det till exempel för anställd_uppgift.

Tabellen anställd_uppgift kommer endast att innehålla två kolumner:

  • Anställnings-ID
  • uppgifts-id

Varje gång vi tilldelar en specifik uppgift till en specifik användare kommer en ny rad att läggas till i den här tabellen. Exempel:

Anställnings-ID uppgifts-id
1 1
1 2
2 3

Tja, uppgiftstabellen borde förlora sin kolumn för anställda_id . Det är bara vettigt om uppgiften bara kan tilldelas en anställd. Om uppgiften kan tilldelas flera anställda, måste denna information lagras i tjänstetabellen för anställda_uppgift .

Relation på tabellnivå

Så här kommer våra nya bord att se ut:

id namn ockupation lön ålder join_date
1 Ivanov Ivan Programmerare 100 000 25 2012-06-30
2 Petrov Petr Programmerare 80 000 23 2013-08-12
3 Ivanov Sergey Testare 40 000 trettio 2014-01-01
4 Rabinovich Moisha Direktör 200 000 35 2015-05-12
5 Kirienko Anastasia Kontors chef 40 000 25 2015-10-10
6 Vaska Katt 1000 3 2018-11-11

Personaltabell ( ej ändrad ) :

Den här tabellen har följande kolumner:

  • id INT
  • namn VARCHAR
  • yrke VARCHAR
  • lön INT
  • ålder INT
  • join_date DATE

Och så här ser uppgiftstabellen ut , tappade kolumnen werknemer_id (markerad i rött):

id emploee_id namn deadline
1 1 Fixa en bugg på frontend 2022-06-01
2 2 Fixa en bugg på backend 2022-06-15
3 5 Köp kaffe 2022-07-01
4 5 Köp kaffe 2022-08-01
5 5 Köp kaffe 2022-09-01
6 (NULL) Städa kontoret (NULL)
7 4 Njut av livet (NULL)
8 6 Njut av livet (NULL)

Den här tabellen har nu bara 3 kolumner:

  • id - unikt uppgiftsnummer (och rader i tabellen)
  • anställd_id - (borttaget)
  • namn - namnet och beskrivningen av uppgiften
  • deadline - tiden fram till vilken uppgiften måste vara klar

Vi har också tjänstetabellen anställd_task , där informationen anställd_id har migrerats från uppgiftstabellen:

Anställnings-ID uppgifts-id
1 1
2 2
5 3
5 4
5 5
(NULL) 6
4 7
6 8

Jag sparade avsiktligt tillfälligt den raderade kolumnen i uppgiftstabellen så att du kan se att data från den har flyttats till tabellen werknemer_uppgift.

En annan viktig punkt är den röda linjen "(NULL) 6" i tabellen werknemer_task. Jag har markerat det i rött eftersom det inte kommer att finnas i tabellen werknemer_task .

Om uppgift 7 är tilldelad användare 4, bör det finnas en rad (4, 7) i tabellen anställd_uppgift.

Om uppgift 6 inte är tilldelad till någon, så kommer det helt enkelt inte att finnas någon post för den i tabellen anställd_uppgift. Så här kommer de slutliga versionerna av dessa tabeller att se ut:

uppgiftstabell :

id namn deadline
1 Fixa en bugg på frontend 2022-06-01
2 Fixa en bugg på backend 2022-06-15
3 Köp kaffe 2022-07-01
4 Köp kaffe 2022-08-01
5 Köp kaffe 2022-09-01
6 Städa kontoret (NULL)
7 Njut av livet (NULL)
8 Njut av livet (NULL)

anställd_uppgiftstabell:

Anställnings-ID uppgifts-id
1 1
2 2
5 3
5 4
5 5
4 7
6 8

Kommunikation på Java-klassnivå

Men med kommunikation på nivå med Entity-klasser har vi en fullständig ordning. Låt oss börja med de goda nyheterna.

För det första har Hibernate en speciell @ManyToMany- anteckning som gör att du väl kan beskriva fallet med en många-till-många-tabellrelation.

För det andra räcker det fortfarande med två Entity-klasser för oss. Vi behöver ingen klass för servicebordet.

Så här kommer våra klasser att se ut. Anställd -klassen i sin ursprungliga form:

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

Och EmployeeTask -klassen i sin ursprungliga form:

@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-anteckning

Jag kommer att utelämna de befintliga fälten i exemplen, men jag kommer att lägga till nya. Så här kommer de att se ut. Anställd klass :

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

}

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

}

Det verkar som att allt är komplicerat, men i själva verket är allt enkelt.

Först använder den @JoinTable -anteckningen (inte att förväxla med @JoinColumn), som beskriver tjänstetabellen för anställda_uppgift.

För det andra beskriver den att kolumnen task_id i tabellen werknemer_task hänvisar till id-kolumnen i uppgiftstabellen.

För det tredje står det att kolumnen anställd_id i tabellen anställd_uppgift hänvisar till id-kolumnen i tabellen anställd.

Faktum är att vi med hjälp av anteckningar beskrev vilken data som finns i tabellen werknemer_task och hur Hibernate ska tolka den.

Men nu kan vi mycket enkelt lägga till (och ta bort) en uppgift till vilken anställd som helst. Och lägg även till vilken artist som helst till vilken uppgift som helst.

Begär exempel

Låt oss skriva ett par intressanta frågor för att bättre förstå hur dessa ManyToMany-fält fungerar. Och de fungerar precis som förväntat.

För det första kommer vår gamla kod att fungera utan ändringar, eftersom regissören hade ett uppgiftsfält tidigare:

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

För det andra, om vi vill tilldela en annan artist till någon uppgift, är det ännu lättare att göra detta:

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

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

Viktig! Som ett resultat av att utföra denna begäran kommer inte bara uppgiften att ha en exekutor-direktör, utan även direktören kommer att ha uppgift nr 101.

För det första kommer fakta om förhållandet mellan regissören och uppgiften i tabellen werknemer_uppgift att lagras som en sträng: (4 101).

För det andra är fälten markerade med @ManyToMany- anteckningar proxyobjekt, och när de nås exekveras alltid en databasfråga.

Så om du lägger till en uppgift till en anställd och sparar information om den anställde i databasen, så kommer uppgiften efter det att ha en ny utförare i listan över utförare.