mesa de serviço

Agora vamos ver outro caso comum - muitos para muitos. Vamos imaginar que temos uma relação muitos-para-muitos entre tarefas e funcionários :

  • Um funcionário na tabela de funcionários pode executar muitas tarefas da tabela de tarefas.
  • Uma tarefa na tabela de tarefas pode ser atribuída a vários funcionários.

Essa relação entre entidades é chamada de muitos-para-muitos. E para implementá-lo no nível SQL, precisamos de uma tabela de serviço adicional. Vamos chamá-lo, por exemplo, employee_task.

A tabela employee_task conterá apenas duas colunas:

  • ID do Empregado
  • task_id

Cada vez que atribuímos uma tarefa específica a um usuário específico, uma nova linha será adicionada a esta tabela. Exemplo:

ID do Empregado task_id
1 1
1 2
2 3

Bem, a tabela de tarefas deve perder sua coluna employee_id . Faz sentido apenas se a tarefa puder ser atribuída a apenas um funcionário. Se a tarefa puder ser atribuída a vários funcionários, essas informações deverão ser armazenadas na tabela de serviço employee_task .

Relacionamento em nível de tabela

Veja como nossas novas tabelas ficarão:

eu ia nome ocupação salário idade data de afiliação
1 Ivanov Ivan Programador 100000 25 30/06/2012
2 Petrov Petr Programador 80000 23 12/08/2013
3 Ivanov Sergei Testador 40000 trinta 2014-01-01
4 Rabinovich Moisha Diretor 200000 35 2015-05-12
5 Kirienko Anastácia Gerente 40000 25 2015-10-10
6 Vaska Gato 1000 3 2018-11-11

Tabela de funcionários ( não alterada ) :

Esta tabela tem as seguintes colunas:

  • id INT
  • nome VARCHAR
  • ocupação VARCHAR
  • salário INT
  • idade INT
  • data_de_entrada DATE

E é assim que fica a tabela de tarefas , perdeu a coluna employee_id (marcada em vermelho):

eu ia id_funcionário nome prazo final
1 1 Corrigir um bug no front-end 01/06/2022
2 2 Corrigir um bug no back-end 15/06/2022
3 5 comprar café 01/07/2022
4 5 comprar café 01/08/2022
5 5 comprar café 01/09/2022
6 (NULO) Limpe o escritório (NULO)
7 4 Aproveite a vida (NULO)
8 6 Aproveite a vida (NULO)

Esta tabela agora tem apenas 3 colunas:

  • id - número de tarefa exclusivo (e linhas na tabela)
  • employee_id - (removido)
  • name - o nome e a descrição da tarefa
  • deadline - o tempo até o qual a tarefa deve ser concluída

Também temos a tabela de serviço employee_task , onde os dados do employee_id foram migrados da tabela task:

ID do Empregado task_id
1 1
2 2
5 3
5 4
5 5
(NULO) 6
4 7
6 8

Propositadamente, salvei temporariamente a coluna excluída na tabela de tarefas para que você possa ver que os dados dela foram movidos para a tabela employee_task.

Outro ponto importante é a linha vermelha "(NULL) 6" na tabela employee_task. Marquei em vermelho porque não estará na tabela employee_task .

Se a tarefa 7 for atribuída ao usuário 4, deve haver uma linha (4, 7) na tabela employee_task.

Se a tarefa 6 não for atribuída a ninguém, simplesmente não haverá registro para ela na tabela employee_task. Veja como serão as versões finais dessas tabelas:

tabela de tarefas :

eu ia nome prazo final
1 Corrigir um bug no front-end 01/06/2022
2 Corrigir um bug no back-end 15/06/2022
3 comprar café 01/07/2022
4 comprar café 01/08/2022
5 comprar café 01/09/2022
6 Limpe o escritório (NULO)
7 Aproveite a vida (NULO)
8 Aproveite a vida (NULO)

tabela empregado_tarefa:

ID do Empregado task_id
1 1
2 2
5 3
5 4
5 5
4 7
6 8

Comunicação no nível de classe Java

Mas com a comunicação no nível das classes de entidades, temos uma ordem completa. Vamos começar com as boas notícias.

Primeiro, o Hibernate tem uma anotação especial @ManyToMany que permite que você descreva bem o caso de um relacionamento de tabela muitos-para-muitos.

Em segundo lugar, duas classes de Entidade ainda são suficientes para nós. Não precisamos de uma classe para a mesa de serviço.

Veja como serão nossas aulas. A classe Employee em sua forma original:

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

E a classe EmployeeTask em sua forma original:

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

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

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

Anotação @ManyToMany

Vou omitir os campos existentes nos exemplos, mas vou adicionar novos. Aqui está como eles serão. Classe de funcionário :

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

}

E a classe 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>();

}

Parece que tudo é complicado, mas na verdade tudo é simples.

Primeiro, ele usa a anotação @JoinTable (não confundir com @JoinColumn), que descreve a tabela de serviço employee_task.

Em segundo lugar, descreve que a coluna task_id da tabela employee_task refere-se à coluna id da tabela task.

Em terceiro lugar, diz que a coluna employee_id da tabela employee_task refere-se à coluna id da tabela Employees.

Na verdade, com a ajuda de anotações, descrevemos quais dados estão contidos na tabela employee_task e como o Hibernate deve interpretá-los.

Mas agora podemos facilmente adicionar (e excluir) uma tarefa para qualquer funcionário. E também adicione qualquer artista a qualquer tarefa.

Solicitar exemplos

Vamos escrever algumas consultas interessantes para entender melhor como esses campos ManyToMany funcionam. E eles funcionam exatamente como esperado.

Primeiro, nosso código antigo funcionará sem alterações, pois o diretor tinha um campo de tarefas antes:

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

Em segundo lugar, se quisermos atribuir outro executor a alguma tarefa, é ainda mais fácil fazer isso:

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

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

Importante! Como resultado da execução desta solicitação, não apenas a tarefa terá um diretor-executor, mas também o diretor terá a tarefa nº 101.

Primeiro, o fato sobre o relacionamento entre o diretor e a tarefa na tabela employee_task será armazenado como uma string: (4,101).

Em segundo lugar, os campos marcados com anotações @ManyToMany são objetos proxy e, quando são acessados, uma consulta ao banco de dados é sempre executada.

Portanto, se você adicionar uma tarefa a um funcionário e salvar informações sobre o funcionário no banco de dados, depois disso a tarefa terá um novo executor na lista de executores.