JPA - ManyToOne

Relacionamento Muitos-para-Um

Este relacionamento informa que existem muitos registros de uma entidade associados a um registro de outra entidade.

Exemplo de relacionamento Um-para-Muitos e Muitos-para-Um bidirecional, no qual uma Pessoa possui vários Telefones:

Script do banco de dados:

CREATE TABLE Pessoa (
  id int NOT NULL auto_increment,
  nome varchar(200),
  cpf varchar(11),
  PRIMARY KEY (id)
);

CREATE TABLE Telefone (
  id int NOT NULL auto_increment,
  tipo varchar(200) NOT NULL,
  numero int NOT NULL,
  pessoa_id int,
  PRIMARY KEY (id)
);

Modelo UML:

Exemplo de relacionamento @ManyToOne.

Neste exemplo definimos que uma Pessoa possui uma lista de Telefones e um Telefone está associado a uma Pessoa, portanto temos um relacionamento bidirecional.

Código fonte das classes com o relacionamento:

Na entidade Pessoa definimos que uma pessoa possui vários telefones através do atributo List<Telefone> telefones e adicionamos a anotação javax.persistence.OneToMany para informar que o relacionamento de Pessoa para Telefone é de Um-para-Muitos, note que nesta anotação definimos a propriedade mappedBy como “pessoa” que é para informar que o atributo com o nome pessoa na entity Telefone que é dona do relacionamento.

package br.universidadejava.jpa.exemplo.modelo;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

/**
 * Classe utilizada para representar uma Pessoa.
 */
@Entity
public class Pessoa implements Serializable {
  private static final long serialVersionUID = -1905907502453138175L;

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  private String nome;
  private String cpf;
  @OneToMany(mappedBy = "pessoa", cascade = CascadeType.ALL)
  private List<Telefone> telefones;

  public String getCpf() {
    return cpf;
  }
  public void setCpf(String cpf) {
    this.cpf = cpf;
  }
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public String getNome() {
    return nome;
  }
  public void setNome(String nome) {
    this.nome = nome;
  }
  public List<Telefone> getTelefones() {
    return telefones;
  }
  public void setTelefones(List<Telefone> telefones) {
    this.telefones = telefones;
  }
}

Na entidade Telefone definimos o atributo Pessoa pessoa e adicionamos a anotação javax.persistence.ManyToOne para definir que o relacionamento de Telefone para Pessoa é de Muitos-para-Um.

package br.universidadejava.jpa.exemplo.modelo;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

/**
 * Classe utilizada para representar um Telefone.
 */
@Entity
public class Telefone implements Serializable {
  private static final long serialVersionUID = 7526502149208345058L;

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  private String tipo;
  private Integer numero;
  @ManyToOne
  private Pessoa pessoa;

  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public Integer getNumero() {
    return numero;
  }
  public void setNumero(Integer numero) {
    this.numero = numero;
  }
  public Pessoa getPessoa() {
    return pessoa;
  }
  public void setPessoa(Pessoa pessoa) {
    this.pessoa = pessoa;
  }
  public String getTipo() {
    return tipo;
  }
  public void setTipo(String tipo) {
    this.tipo = tipo;
  }
}

javax.persistence.ManyToOne

Esta anotação define uma associação com outra entidade que tenha a multiplicidade de muitos-para-um.

Propriedades Descrição
cascade As operações que precisam ser refletidas no alvo da associação.
fetch Informa se o alvo da associação precisa ser obtido apenas quando for necessário ou se sempre deve trazer.
optional Informa se a associação é opcional.
targetEntity A classe entity que é alvo da associação.

Também podemos adicionar uma tabela para realizar o relacionamento unidirecional de um-para-muitos e o relacionamento muitos-para-muitos, normalmente utilizamos está alternativa como uma forma de normalizar os dados evitando duplicar o conteúdo dos registros.

Nesse exemplo queremos utilizar a entidade Telefone com as entidades Pessoa e Aluno, ou seja, Pessoa possui uma lista de Telefones e Aluno possui uma lista de Telefones, mas o telefone não sabe para quem ele está associado. Este tipo de relacionamento é unidirecional de um-para-muitos.

Exemplo de relacionamento com JoinTable.

Na base de dados iremos criar as tabelas Pessoa, Telefone e Aluno, também iremos criar duas tabelas de associação chamadas Pessoa_Telefone e Aluno_Telefone:

CREATE TABLE Pessoa (
  id int NOT NULL auto_increment,
  nome varchar(200),
  cpf varchar(11),
  PRIMARY KEY (id)
);

CREATE TABLE Aluno (
  id int NOT NULL auto_increment,
  nome varchar(200),
  matricula int,
  PRIMARY KEY (id)
);

CREATE TABLE Telefone (
  id int NOT NULL auto_increment,
  tipo varchar(200) NOT NULL,
  numero int NOT NULL,
  PRIMARY KEY (id)
);

CREATE TABLE Pessoa_Telefone (
  pessoa_id int,
  telefone_id int
);

CREATE TABLE Aluno_Telefone (
  aluno_id int,
  telefone_id int
);

Na entidade Pessoa definimos que uma pessoa possui vários telefones através do atributo List<Telefone> telefones e adicionamos a anotação javax.persistence.OneToMany para informar que o relacionamento de Pessoa para Telefone é de Um-para-Muitos.

Para informar que vamos utilizar a tabela PESSOA_TELEFONE para realizar a associação entre as tabelas PESSOA e TELEFONE utilizamos a anotação javax.persistence.JoinTable.

Para informar que a coluna PESSOA_ID da tabela PESSOA_TELEFONE é a coluna chave estrangeira para a tabela PESSOA e para informar que a coluna TELEFONE_ID da tabela PESSOA_TELEFONE é a chave estrangeira para a tabela TELEFONE utilizamos a anotação javax.persistence.JoinColumn.

package br.universidadejava.jpa.exemplo.modelo;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;

/**
 * Classe utilizada para representar uma Pessoa.
 */
@Entity
public class Pessoa implements Serializable {
  private static final long serialVersionUID = -1905907502453138175L;

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  private String nome;
  private String cpf;
  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
  @JoinTable(name="PESSOA_TELEFONE",
             joinColumns={@JoinColumn(name = "PESSOA_ID")},
             inverseJoinColumns={@JoinColumn(name = "TELEFONE_ID")})
  private List<Telefone> telefones;

  public String getCpf() {
    return cpf;
  }
  public void setCpf(String cpf) {
    this.cpf = cpf;
  }
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public String getNome() {
    return nome;
  }
  public void setNome(String nome) {
    this.nome = nome;
  }
  public List<Telefone> getTelefones() {
    return telefones;
  }
  public void setTelefones(List<Telefone> telefones) {
    this.telefones = telefones;
  }
}

javax.persistence.JoinTable

Esta anotação é utilizada para definir uma tabela que será utilizada na associação de um-para-muitos ou de muitos-para-muitos.

Propriedades Descrição
catalog O catalogo da tabela.
inverseJoinColumns Chave estrangeira para realizar a associação com a tabela que não é dona do relacionamento.
joinColumns Chave estrangeira para realizar a associação com a tabela que é dona do relacionamento.
name Nome da tabela de associação.
schema Esquema da tabela.
uniqueConstraints Regras que podem ser adicionadas na tabela.

Na entidade Aluno definimos que um aluno possui vários telefones através do atributo List<Telefone> telefones e adicionamos a anotação javax.persistence.OneToMany para informar que o relacionamento de Aluno para Telefone é de Um-para-Muitos.

Para informar que vamos utilizar a tabela ALUNO_TELEFONE para realizar a associação entre as tabelas ALUNO e TELEFONE utilizamos a anotação javax.persistence.JoinTable.

Para informar que a coluna ALUNO_ID da tabela ALUNO _TELEFONE é a coluna chave estrangeira para a tabela ALUNO e para informar que a coluna TELEFONE_ID da tabela ALUNO _TELEFONE é a chave estrangeira para a tabela TELEFONE utilizamos a anotação javax.persistence.JoinColumn.

package br.universidadejava.jpa.exemplo.modelo;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;

/**
 * Classe utilizada para representar uma entidade Aluno.
 */
@Entity
public class Aluno import Serializable {
  private static final long serialVersionUID = -4292880217218734067L;

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  private String nome;
  private Long matricula;
  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
  @JoinTable(name="ALUNO_TELEFONE",
             joinColumns={@JoinColumn(name = "ALUNO_ID")},
             inverseJoinColumns={@JoinColumn(name = "TELEFONE_ID")})
  private List<Telefone> telefones;

  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public Long getMatricula() {
    return matricula;
  }
  public void setMatricula(Long matricula) {
    this.matricula = matricula;
  }
  public String getNome() {
    return nome;
  }
  public void setNome(String nome) {
    this.nome = nome;
  }
  public List<Telefone> getTelefones() {
    return telefones;
  }
  public void setTelefones(List<Telefone> telefones) {
    this.telefones = telefones;
  }
}

Agora vamos declarar a entidade Telefone, note que esta entidade não conhece as associações que são criadas para ela.

package br.universidadejava.jpa.exemplo.modelo;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
 * Classe utilizada para representar um Telefone.
 */
@Entity
public class Telefone implements Serializable {
  private static final long serialVersionUID = 7526502149208345058L;

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  private String tipo;
  private Integer numero;

  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public Integer getNumero() {
    return numero;
  }
  public void setNumero(Integer numero) {
    this.numero = numero;
  }
  public String getTipo() {
    return tipo;
  }
  public void setTipo(String tipo) {
    this.tipo = tipo;
  }
}

Para testar o cadastro de Aluno e Telefone vamos criar uma classe AlunoDAO onde iremos salvar, alterar, consultar por id e apagar os registro do aluno e telefone.

package br.universidadejava.jpa.exemplo.dao;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import br.universidadejava.jpa.exemplo.modelo.Aluno;

/**
 * Classe DAO para manipular as informações do Aluno no banco de dados.
 */
public class AlunoDAO {
  private EntityManager getEntityManager() {
    EntityManagerFactory factory = null;
    EntityManager entityManager = null;
    try {
      //Obtem o factory a partir da unidade de persistencia.
      factory = Persistence.createEntityManagerFactory("UnidadeDePersistencia");
      //Cria um entity manager.
      entityManager = factory.createEntityManager();
    } finally {
      factory.close();
    }
    return entityManager;
  }

  public Aluno consultarPorId(Long id) {
    EntityManager entityManager = getEntityManager();
    Aluno aluno = null;
    try {
      aluno = entityManager.find(Aluno.class, id);
    } finally {
      entityManager.close();
    }
    return aluno;
  }

  public Aluno salvar(Aluno aluno) {
    EntityManager entityManager = getEntityManager();
    try {
      // Inicia uma transação com o banco de dados.
      entityManager.getTransaction().begin();
      System.out.println("Salvando as informações do aluno.");
      // Verifica se o aluno ainda não está salvo no banco de dados.
      if(aluno.getId() == null) {
        entityManager.persist(aluno);
      } else {
        aluno = entityManager.merge(aluno);
      }
      // Finaliza a transação.
      entityManager.getTransaction().commit();
    } catch(Exception ex) {
      entityManager.getTransaction().rollback();
    } finally {
      entityManager.close();
    }
    // Retorna o aluno salvo.
    return aluno;
  }

  public void apagar(Long id) {
    EntityManager entityManager = getEntityManager();
    try {
      // Inicia uma transação com o banco de dados.
      entityManager.getTransaction().begin();
      // Consulta o aluno na base de dados através do seu ID.
      Aluno aluno = entityManager.find(Aluno.class, id);
      System.out.println("Excluindo o cliente: " + aluno.getNome());
      // Remove o aluno da base de dados.
      entityManager.remove(aluno);
      // Finaliza a transação.
      entityManager.getTransaction().commit();
    } catch(Exception ex) {
      entityManager.getTransaction().rollback();
    } finally {
      entityManager.close();
    }
  }
}

Vamos criar a classe AlunoDAOTeste que utilizará a classe AlunoDAO para salvar, alterar, consultar por id e apagar os registros de aluno e telefone:

package br.universidadejava.jpa.exemplo.dao;

import java.util.ArrayList;
import java.util.List;
import br.universidadejava.jpa.exemplo.modelo.Aluno;
import br.universidadejava.jpa.exemplo.modelo.Telefone;

/**
 * Classe utilizada para testar as operações do banco de dados referente ao Aluno.
 */
public class AlunoDAOTeste {
  public static void main(String[] args) {
    AlunoDAO dao = new AlunoDAO();

    //Cria uma aluno.
    Aluno aluno = new Aluno();
    aluno.setNome("Rafael");
    aluno.setMatricula(123456L);

    //Cria o telefone residencial do aluno.
    Telefone telefone = new Telefone();
    telefone.setTipo("RES");
    telefone.setNumero(12345678);
    //Cria o telefone celular do aluno.
    Telefone telefone2 = new Telefone();
    telefone2.setTipo("CEL");
    telefone2.setNumero(87654321);

    //Cria uma lista de telefones e guarda dentro do aluno.
    List<Telefone> telefones = new ArrayList<Telefone>();
    telefones.add(telefone);
    telefones.add(telefone2);
    aluno.setTelefones(telefones);

    System.out.println("Salva as informações do aluno.");
    aluno = dao.salvar(aluno);

    System.out.println("Consulta o aluno que foi salvo.");
    Aluno alunoConsultado = dao.consultarPorId(aluno.getId());
    System.out.println(aluno.getNome());
    for(Telefone tel : aluno.getTelefones()) {
      System.out.println(tel.getTipo() + " - " + tel.getNumero());
    }

    //Cria o telefone comercial do aluno.
    Telefone telefone3 = new Telefone();
    telefone3.setTipo("COM");
    telefone3.setNumero(55554444);
    //Adiciona o novo telefone a lista de telefone do aluno.
    alunoConsultado.getTelefones().add(telefone3);

    System.out.println("Atualiza as informações do aluno.");
    alunoConsultado = dao.salvar(alunoConsultado);
    System.out.println(alunoConsultado.getNome());
    for(Telefone tel : alunoConsultado.getTelefones()) {
      System.out.println(tel.getTipo() + " - " + tel.getNumero());
    }

    System.out.println("Apaga o registro do aluno.");
    dao.apagar(alunoConsultado.getId());
  }
}

Quando executamos a classe AlunoDAOTeste temos a seguinte saída no console:

Salva as informações do aluno:

Hibernate: insert into Aluno (matricula, nome) values (?, ?)
Hibernate: insert into Telefone (numero, tipo) values (?, ?)
Hibernate: insert into Telefone (numero, tipo) values (?, ?)
Hibernate: insert into ALUNO_TELEFONE (ALUNO_ID, TELEFONE_ID) values (?, ?)
Hibernate: insert into ALUNO_TELEFONE (ALUNO_ID, TELEFONE_ID) values (?, ?)

Consulta o aluno que foi salvo:

Hibernate: select aluno0_.id as id21_1_, aluno0_.matricula as matricula21_1_, aluno0_.nome as nome21_1_, telefones1_.ALUNO_ID as ALUNO1_3_, telefone2_.id as TELEFONE2_3_, telefone2_.id as id18_0_, telefone2_.numero as numero18_0_, telefone2_.tipo as tipo18_0_ from Aluno aluno0_ left outer join ALUNO_TELEFONE telefones1_ on aluno0_.id=telefones1_.ALUNO_ID left outer join Telefone telefone2_ on telefones1_.TELEFONE_ID=telefone2_.id where aluno0_.id=?
Rafael
RES - 12345678
CEL - 87654321

Atualiza as informações do aluno:

Hibernate: select aluno0_.id as id38_1_, aluno0_.matricula as matricula38_1_, aluno0_.nome as nome38_1_, telefones1_.ALUNO_ID as ALUNO1_3_, telefone2_.id as TELEFONE2_3_, telefone2_.id as id35_0_, telefone2_.numero as numero35_0_, telefone2_.tipo as tipo35_0_ from Aluno aluno0_ left outer join ALUNO_TELEFONE telefones1_ on aluno0_.id=telefones1_.ALUNO_ID left outer join Telefone telefone2_ on telefones1_.TELEFONE_ID=telefone2_.id where aluno0_.id=?
Hibernate: insert into Telefone (numero, tipo) values (?, ?)
Hibernate: delete from ALUNO_TELEFONE where ALUNO_ID=?
Hibernate: insert into ALUNO_TELEFONE (ALUNO_ID, TELEFONE_ID) values (?, ?)
Hibernate: insert into ALUNO_TELEFONE (ALUNO_ID, TELEFONE_ID) values (?, ?)
Hibernate: insert into ALUNO_TELEFONE (ALUNO_ID, TELEFONE_ID) values (?, ?)
Rafael
RES - 12345678
CEL - 87654321
COM - 55554444

Apaga o registro do aluno:

Hibernate: select aluno0_.id as id55_1_, aluno0_.matricula as matricula55_1_, aluno0_.nome as nome55_1_, telefones1_.ALUNO_ID as ALUNO1_3_, telefone2_.id as TELEFONE2_3_, telefone2_.id as id52_0_, telefone2_.numero as numero52_0_, telefone2_.tipo as tipo52_0_ from Aluno aluno0_ left outer join ALUNO_TELEFONE telefones1_ on aluno0_.id=telefones1_.ALUNO_ID left outer join Telefone telefone2_ on telefones1_.TELEFONE_ID=telefone2_.id where aluno0_.id=?
Hibernate: delete from ALUNO_TELEFONE where ALUNO_ID=?
Hibernate: delete from Telefone where id=?
Hibernate: delete from Telefone where id=?
Hibernate: delete from Telefone where id=?
Hibernate: delete from Aluno where id=?