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.