Jakarta Persistence (JPA)

Como mapear relacionamentos ManyToOne com Jakarta Persistence (JPA)

Mapeamento-ManyToOne-JPA

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:

Diagrama de Entidades e Relacionamentos

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:

  1. O nome do atributo que faz a referência (nesse caso, o atributo departamento da classe Funcionario)
  2. Underline
  3. O nome da coluna para a qual o atributo @Id da classe referenciada (nesse caso, classe Departamento) 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.

Fundador da AlgaWorks, uma das principais escolas de desenvolvimento Java e front-end do Brasil. Autor de diversos livros e cursos de Java e front-end. Palestrante no JavaOne San Francisco em 2016, a maior conferência de Java do mundo. Programador desde os 14 anos de idade (1995), quando desenvolveu o primeiro jogo de truco online e multiplayer (que ficou bem famoso na época).

Olá,

o que você achou deste conteúdo? Conte nos comentários.

Junte-se a mais de 100.000 pessoas

Entre para nossa lista e receba conteúdos exclusivos e com prioridade

Você se Inscreveu com Sucesso!