No modelo relacional, uma associação muitos-para-um (N:1) é quando temos várias tuplas de uma tabela referenciando uma tupla de uma tabela qualquer.
Por exemplo, veja este modelo com duas tabelas:
A tabela funcionario
tem um relacionamento muitos-para-um com a tabela departamento
.
Isso quer dizer que muitos funcionários referenciam um departamento. Em outras palavras, um único departamento pode ter muitos funcionários.
Mas como mapear esse relacionamento em uma entidade do Jakarta Persistence (JPA)?
É sobre isso que você vai aprender, na prática, neste artigo.
Mapeando as entidades
Antes de falar do relacionamento, vamos mapear as entidades do nosso modelo de domínio, começando pela entidade Departamento
, que representa a tabela departamento
.
@Entity @Table(name = "departamento") public class Departamento { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer codigo; private String nome; // getters e setters omitidos }
Eu não vou entrar em detalhes sobre mapeamento básico. Estou considerando que você já conhece o mínimo de JPA. Caso não conheça, é só ler o nosso tutorial de JPA antes de continuar.
Agora veja o código da entidade Funcionario
, que representa a tabela funcionario
.
@Entity @Table(name = "funcionario") public class Funcionario { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer codigo; private String nome; // getters e setters omitidos }
Maravilha! Nenhuma novidade sobre JPA até aqui, né?
A anotação @ManyToOne
Mas nós precisamos mapear o relacionamento que funcionário tem com departamento. Muitos funcionários podem estar associados a um departamento, lembra?
Para fazer isso nós temos que identificar quem é o dono da relação. Neste caso, a tabela funcionario
é a proprietária, já que a coluna da foreign key está nela.
Então nós adicionamos um novo atributo na classe Funcionario
e mapeamos ele com a anotação @ManyToOne
:
@Entity @Table(name = "funcionario") public class Funcionario { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer codigo; private String nome; // adicionamos aqui o mapeamento muitos-para-um @ManyToOne private Departamento departamento; // getters e setters omitidos }
Simples assim!
A anotação @ManyToOne
indica que temos um relacionamento muitos-para-um entre Funcionario
e Departamento
.
Testando o mapeamento
Com o mapeamento feito, podemos testá-lo!
Primeiro, vamos criar uma classe Java com um método main
e obter uma instância de EntityManager
:
public class InclusaoMain { public static void main(String[] string) { EntityManagerFactory entityManagerFactory = Persistence .createEntityManagerFactory("FolhaPagamentoPU"); EntityManager entityManager = entityManagerFactory.createEntityManager(); // TODO vamos persistir e consultar entidades aqui entityManager.close(); entityManagerFactory.close(); } }
Beleza! Agora podemos instanciar e persistir instâncias de departamentos e funcionários:
// instanciamos um departamento Departamento departamento1 = new Departamento(); departamento1.setNome("Vendas"); // instanciamos dois funcionários e atribuímos o departamento Funcionario funcionario1 = new Funcionario(); funcionario1.setNome("João das Couves"); funcionario1.setDepartamento(departamento1); Funcionario funcionario2 = new Funcionario(); funcionario2.setNome("Maria Abadia"); funcionario2.setDepartamento(departamento1); // iniciamos uma transação e persistimos os objetos entityManager.getTransaction().begin(); entityManager.persist(departamento1); entityManager.persist(funcionario1); entityManager.persist(funcionario2); entityManager.getTransaction().commit();
É importante que o departamento já tenha sido persistido antes de tentar persistir um funcionário, por isso fizemos isso na instrução anterior à persistência do funcionário.
E para conferir se deu tudo certo, podemos consultar os funcionários e acessar a associação com o departamento através do método getter.
public class ConsultaMain { public static void main(String[] string) { EntityManagerFactory entityManagerFactory = Persistence .createEntityManagerFactory("FolhaPagamentoPU"); EntityManager entityManager = entityManagerFactory.createEntityManager(); // busca o funcionário de código 1 Funcionario funcionario1 = entityManager.find(Funcionario.class, 1); // busca o funcionário de código 2 Funcionario funcionario2 = entityManager.find(Funcionario.class, 2); // obtém o nome do departamento associado aos funcionários System.out.printf("Funcionário %s está no departamento %s%n", funcionario1.getNome(), funcionario1.getDepartamento().getNome()); System.out.printf("Funcionário %s está no departamento %s%n", funcionario2.getNome(), funcionario2.getDepartamento().getNome()); entityManager.close(); entityManagerFactory.close(); } }
Nome da coluna da chave estrangeira
Agora que você viu tudo funcionando, pode ser que esteja perguntando:
“Por que não precisamos configurar o nome da coluna da chave estrangeira que relaciona funcionário com departamento?”
Essa é uma excelente pergunta, que felizmente será respondida agora.
Por padrão, o JPA usou o nome departamento_codigo
para gerar os SQLs, mesmo que a gente não tenha especificado esse nome.
Quando fazemos mapeamento com @ManyToOne
e não informamos o nome da coluna da chave estrangeira, então a implementação JPA assume um padrão.
Esse padrão é montado em três partes:
- O nome do atributo que faz a referência (nesse caso, o atributo
departamento
da classeFuncionario
) - Underline
- O nome da coluna para a qual o atributo
@Id
da classe referenciada (nesse caso, classeDepartamento
) está apontando
A implementação do JPA não vai ler nada disso do banco de dados, e sim do mapeamento das entidades!
Como o nome do atributo que referencia o departamento dentro do funcionário se chama departamento
e o nome do atributo que representa a chave primária de Departamento
é codigo
, então temos por padrão o nome da coluna departamento_codigo
.
Se o atributo codigo
da entidade Departamento
fosse configurado para ter um outro nome de coluna, usando a anotação @Column
, então o nome de coluna configurado seria usado para compor o nome da coluna da chave estrangeira da relação.
Por exemplo, imagine a classe Departamento
com esse mapeamento no atributo codigo
:
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "cod") private Integer codigo;
Nesse caso, o nome da coluna da chave estrangeira (caso não fosse especificado) para a associação entre Funcionario
e Departamento
seria departamento_cod
.
Caso o nome da coluna da sua chave estrangeira não siga o padrão ou caso você prefira deixar isso explícito, você ainda pode usar a anotação @JoinColumn
especificando o nome da coluna no atributo name
.
@ManyToOne @JoinColumn(name = "departamento_codigo") private Departamento departamento;
Conclusão
Legal, né?
O JPA nos ajuda bastante a fazer mapeamento objeto-relacional sem precisar de muito código, ao mesmo tempo que nos permite customizar praticamente tudo que a gente precisa.
O relacionamento muitos-para-um é bem simples, mas como você viu, tem alguns detalhes importantes que você aprendeu aqui neste artigo.
Agora, me conta aí nos comentários: qual foi a novidade que você aprendeu neste artigo e mais gostou?
E se você tem interesse em aprofundar mais em JPA, conheça o nosso curso online Especialista JPA.
Um abraço e até a próxima!
PS1: Baixe o código-fonte dos exemplos em nosso GitHub: https://github.com/algaworks/artigo-jpa-manytoone.
PS2: Este artigo foi co-autorado por Alexandre Afonso.
Olá,
o que você achou deste conteúdo? Conte nos comentários.