27 de março de 2011

JPA 2.0: Criando tabelas - Parte 3

Dando sequência as postagens sobre JPA 2.0, nessa vamos entender como funciona a anotação @ManyToMany. Esta anotação serve para gerar uma tabela que resolva um relacionamento  N - N entre duas classes. No caso de usar o @ManyToMany, você não vai precisar criar uma classe intermediária que será anotada, pelo contrário, você deverá apenas duas classes, por exemplo, uma Venda.java e uma Produto.java, nesse relacionamento uma venda pode ter N produtos, e um produto pode estar em N vendas. Sendo assim, em Venda teremos uma lista de produtos, e em Produto teremos uma lista de vendas. Então vamos ao código.
Venda.java
@Entity
public class Venda implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long codigo;

    @ManyToMany
    private List<Produto> produtos;

    // gerar os getters e setters
}

Produto.java
@Entity
public class Produto implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long codigo;

    @ManyToMany
    private List<Venda> vendas;

    // gerar os getters e setters
}

O resultado dessas duas classes será:



Isso acontece porque o JPA não sabe qual das classes será a principal, o que pode acontecer é que se caso você insira uma venda com uma lista de produtos, ela irá inserir em VENDA_PRODUTO, e se você inserir um produto com uma lista de vendas, irá inserir em PRODUTO_VENDA, mas como isso não faz sentido no caso de produtos e vendas, na classe Produto.java, altere a anotação @ManyToMany para:
@ManyToMany(mappedBy="produtos")
    private List<Venda> vendas;
O resultado será:


Mas como eu sou chata e não gosto desse estilo de nomenclatura que JPA usa pra gerar as tabelas, veja as colunas da tabela VENDA_PRODUTO: Venda_CODIGO e produtos_CODIGO, tem uma forma de você nomear a tabela e as colunas como quiser, então em Venda.java altere a anotação @ManyToMany para:
@ManyToMany
    @JoinTable(name="VENDA_HAS_PRODUTO",
        joinColumns=@JoinColumn(name="VENDA_CODIGO"),
        inverseJoinColumns=@JoinColumn(name="PRODUTO_CODIGO"))
    private List<Produto> produtos;

Veja que a anotação @JoinTable é a responsável por dar essa possibilidade de nomear a tabela e as colunas, vamos entender ela: a propriedade name se refere ao nome da tabela, a propriedade joinColumns é responsável por nomear a chave primária correspondente a tabela de VENDA, e a inverseJoinColumns renomeia a chave primária de PRODUTO, ficando assim:



Caso seu sistema não queira gerar as tabelas, apenas crie uma classe qualquer que faça a conexão com o banco, isto será suficiente, assim como mostra abaixo:
public class DAO {
    @PersistenceContext(unitName="ManyToManyPU")
    private EntityManager em;
}
Lembrando que o ManyToManyPU é meu persistence-unit que está no meu persistence.xml, veja o que você configurou em seu persistence.

Só mais um detalhe, muitas pessoas tem dúvidas de como será feito a inserção de dados dentro dessa tabela intermediária, isso é mais simples do que imaginam... Em Venda nós não temos uma lista de produtos?? então é bem isso, para inserir uma venda, você deve setar uma lista de produtos nela, e dar um em.merge(venda) ou em.persiste(venda), e ela já insere a venda na tabela de venda, e os relacionamentos dos produtos da lista com a venda na tabela intermediária. 

Para mais informações, veja: ManyToMany (Java EE 6)

 ;)

8 comentários:

  1. Ótimo Exemplo Andii...
    Parabéns!!

    ResponderExcluir
  2. Muito bom post, parabéns pela qualidade!

    Porém tenho uma dúvida, não sei se é aplicada para esse post mas no caso de querer armazenar a quantidade de produtos por venda, eu deveria ter na tabela gerada do relacionamento um campo com a quantidade certo? como isso seria feito? Exite um outro artigo onde consigo visualizer isso?

    Abraço e parabéns pelo Blog.
    Atte.,
    Adalto

    ResponderExcluir
  3. Adalto,desculpa pela demora a te responder. Acredito que para o seu caso, esta postagem não adianta muito, tentei pesquisar uma outra solução que te ajudasse, mas não consegui encontrar. Mas posso te garantir que eu já vi uma solução para isso que vc quer fazer, só não consegui encontrar onde foi.

    ResponderExcluir
  4. OI.
    Muito bom..
    Assisti as video aulas... muito boas..

    Fiz o procedimento como foi orientado no video e esta aparecendo a seguinte mensagem:

    Jul 03, 2012 11:57:27 PM org.hibernate.annotations.common.Version
    INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
    Jul 03, 2012 11:57:28 PM org.hibernate.Version logVersion
    INFO: HHH000412: Hibernate Core {4.1.4.Final}
    Jul 03, 2012 11:57:28 PM org.hibernate.cfg.Environment
    INFO: HHH000206: hibernate.properties not found
    Jul 03, 2012 11:57:28 PM org.hibernate.cfg.Environment buildBytecodeProvider
    INFO: HHH000021: Bytecode provider name : javassist
    Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named ProdutoBean
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:69)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:47)
    at novo_projeto.Novo_Projeto.main(Novo_Projeto.java:27)

    Sera que é incompatibilidade entre a versao do netbeans e o hibernate?

    ResponderExcluir
    Respostas
    1. Jucilene, não sei de que video vc está falando (não tem nenhum video feito por mim), mas este erro diz que não existe um persistence unit com o nome de ProdutoBean, veja se esta postagem pode te ajudar: http://javasemcafe.blogspot.com.br/2011/02/jpa-20-criando-tabelas-parte-1.html

      Excluir
  5. andii.brunetta, gostei também da sua explicação aqui...quase um ano depois nem sei se vou conseguir respostas...mas....

    Se alguém puder ajudar, tenho esse mesmo modelo apresentado aqui com um banco de dados existente, ou seja, tenho uma tabela VENDA_PRODUTO no PostgreSQL.

    Na minha aplicação como vou fazer um método para listar as vendas de um terminado produto na relação @ManyToMany ?

    ResponderExcluir
  6. O meu não está criando as tabelas e eu uso postgres, eu tenho que fazer alguma conf no postgres ?

    ResponderExcluir

Deixe seu comentário... ;)