JPA @OneToMany e @ManyToOne Unidirecional e Bidirecional

EXISTE UMA NOVA VERSÃO DESSE POST.
CLICK AQUI: http://uaihebert.com/?p=1622&page=20

Olá, bom dia.

Vamos ver hoje o relacionamento @OneToMany e @ManyToOne Unidirecional e Bidirecional.

Podemos fazer o relacionamento @OneToMany de dois modos, utilizando a anotação @JoinColumn ou a anotação @JoinTable.

Irei utilizar o código de posts passados. Se você quiser montar um ambiente para executar o código do post, você pode olhar os outros posts sobre o assunto: @OneToOne Unidirecional e Bidirecional, Mapeando Duas Tabelas em uma Classe, Mapeando Datas (Date) e Enum, Chave Primária Composta, SequenceGenerator, TableGenerator – Chave Primária Simples, Auto Create Schema Script com: Ant, Hibernate 3 e JPA 2, Tutorial Hibernate 3 com JPA 2.

Vou utilizar um simples exemplo, um Customer (cliente) pode ter vários cachorros, mas um Dog (cachorro) só pode ter um Customer.

Vamos ao código da classe Dog:

package com;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name="DOG")
@SequenceGenerator(name="DOG_SEQUENCE", sequenceName="DOG_SEQUENCE", allocationSize=1, initialValue=0)
public class Dog {

    public Dog(){

    }

    public Dog(String name){
        this.name = name;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="DOG_SEQUENCE")
    private int id;

    @Column
    private String name;

    //Getters and Setters
}

Repare que como nosso código é Unidirecional ele não faz menção do Customer.

Vamos ver agora, como ficará nossa classe Customer utilizando o @JoinTable:

package com;

import java.util.List;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name = "CUSTOMER")
@SequenceGenerator(name = "CUSTOMER_SEQUENCE", sequenceName = "CUSTOMER_SEQUENCE", allocationSize = 1, initialValue = 0)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_SEQUENCE")
    private int id;

    @Column
    private String name;

    @OneToOne(cascade = CascadeType.ALL, optional = true, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name="USER_ID", nullable=true)
    private User user;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinTable(name="CUSTOMER_HAS_DOGS", joinColumns={@JoinColumn(name="CUSTOMER_ID", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="DOG_ID", referencedColumnName="id")})
    private List dogs;

    //Getters and Setters
}

A anotação @JoinTable é bem simples e seu uso bem prático:

  • “name” – indica qual será a tabela que realizará a junção entre Customer e Dog (Por padrão colocamos o lado dominante como primeiro nome na tabela).
  • “joinColumns” – informa ao JPA um conjunto de chaves (um array separado por vírgulas) a ser utilizado para se identificar um registro. Poderia ser, por exemplo, o ID e o nome (“name”).
  • “@JoinColumn” – aponta uma coluna que servirá de chave primária na tabela de relacionamento. “name” é o nome que a coluna da tabela terá, e “referencedColumnName” é a chave primária da tabela dona do relacionamento; em nosso caso utilizamos id da tabela Customer. (Ao final do post veremos o real lado dominante desse relacionamento. Utilizo o Customer apenas para ficar didaticamente mais fácil)
  • “inverseJoinColumns” – faz o mapeamento das colunas da tabela do lado “dominado/fraco”.

Vamos inserir as informações no banco de dados? Abaixo o código que irá criar um relacionamento de um Customer com Dog (para configurar sua aplicação, verifique os links no começo do post e siga os passos):

package com;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            List dogs = new ArrayList();
            dogs.add(new Dog("Terminator"));
            dogs.add(new Dog("Fluffy"));
            dogs.add(new Dog("Bacon"));

            Customer customer = new Customer();
            customer.setName("Arnold");
            customer.setDogs(dogs);

            em.persist(customer);

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

Olhe os resultados:

Imagem Console.
Imagem banco de dados.

O JPA buscou o valor da sequence para cada objeto a ser inserido, inseriu cada registro na tabela, e depois fez o relacionamento na tabela “CUSTOMER_HAS_DOGS”.

Para utilizar o mapeamento @OneToMany sem uma tabela adicional, basta utilizar a anotação @JoinColumn ao invés de @JoinTable. Veja como nossa classe Customer ficará:

package com;

import java.util.List;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name = "CUSTOMER")
@SequenceGenerator(name = "CUSTOMER_SEQUENCE", sequenceName = "CUSTOMER_SEQUENCE", allocationSize = 1, initialValue = 0)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_SEQUENCE")
    private int id;

    @Column
    private String name;

    @OneToOne(cascade = CascadeType.ALL, optional = true, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name="USER_ID", nullable=true)
    private User user;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinColumn(name="CUSTOMER_ID")
    private List dogs;

    //Getters and Setters
}

Agora a anotação @JoinColumn indica qual será a coluna utilizada para identificação.

Apague o SCHEMA do database antigo, crie novamente e veja o resultado após executar a classe Main:

Imagem do Console.
Imagem do banco de dados.

Veja que agora não existe mais uma tabela entre Customer e Dog. Na tabela Dog, foi criado uma coluna que fará essa referência.

E para tornar nosso relacionamento Bidirecional, vamos alterar a classe Dog:

package com;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name="DOG")
@SequenceGenerator(name="DOG_SEQUENCE", sequenceName="DOG_SEQUENCE", allocationSize=1, initialValue=0)
public class Dog {

    public Dog(){

    }

    public Dog(String name){
        this.name = name;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="DOG_SEQUENCE")
    private int id;

    @Column
    private String name;

    @ManyToOne(cascade=CascadeType.ALL)
    private Customer owner;    

    //Getters and Setters
}

A explicação da diferença entre Unidirecional e Bidirecional você encontra aqui: @OneToOne Unidirecional e Bidirecional.

Por padrão, a anotação @ManyToOne deve ser a dominante no relacionamento com @OneToMany. Você poderá notar que não existe a opção “mappedBy” para na anotação @ManyToOne, mas na anotação @OneToMany existe.

Para praticar, você poderia passar todas as configurações para a anotação @ManyToOne e colocar o parâmetro mappedBy em @OneToMany.

Espero que o post te hoje possa te ajudar.

Dúvida/Sugestão basta postar.

Até a próxima.

14 thoughts on “JPA @OneToMany e @ManyToOne Unidirecional e Bidirecional

  1. Blz, boas dicas!

    Estou utilizando JPA provider EclipseLink a pouco tempo, mapiei uma implementação do BD (MySQL) com o Entities from Tables na IDE Eclipse.

    Quando faço desta forma tenho que alterar alguma coisa nas classes (metadados/@anotações) mapeadas.
    Quando tento persistir um objeto, recebo uma exceção relacionada a integridade referencial.

    se vc puder dar uma dica aí :)

    Postei no Guj tb:
    http://guj.com.br/java/285089-jpa-eclipselink-entities-from-tables

    • Miltex, boa tarde.

      Eu até vi que você postou no guj.

      Infelizmente não tenho nenhuma dica.

      Eu sempre crio a classe e faço o mapeamento na unha.

      Sorry.

Leave a Comment