Primeiramente, gostaria de agradecer a todos que têm seguido e contribuído com a divulgação deste meu humilde blog e, pelos positivos feedbacks que tenho recebido. Fico feliz não apenas por apreciarem o conteúdo que disponibilizo, mas por contribuírem com a divulgação do amplo ecossistema Java, que sustenta desde grandes organizações até muitos de nós rs!
Neste post vou dar continuidade ao uso de relacionamentos com JPA, neste, verificaremos como implementar uma relação do tipo N:N (muitos para muitos - @ManyToMany). Para demonstrar este relacionamento, darei continuidade no projeto já iniciado nos posts de JPA anteriormente, até então temos a relação 1:N entre Produto e Categoria.
Contexto: do que já foi implementado até o momento, consegue-se categorizar os produtos, logo, facilmente obtemos todos os produtos de uma dada categoria. Imagine que queremos catalogar todos os produtos que um usuário (cliente) visite, para que futuramente, possa-se traçar um perfil de consumo. O que envolve nisto: logo, um usuário poderá ver mais de um produto, e um produto será visualizado por mais de um usuário, o que caracteriza a relação N:N.
Modelagem
Como dito, teremos um relacionamento N:N entre cliente e produto, para contemplar este requisito, nossa modelagem ficará da seguinte forma:
Para efetuarmos as alterações, temos o seguinte sql:
create table cliente ( id_cliente serial, nome varchar(70), primary key (id_cliente)); create table cliente_produto_rel ( id_cliente integer, id_produto integer, primary key (id_cliente, id_produto), foreign key (id_cliente) references cliente (id_cliente), foreign key (id_produto) references produto (id_produto));
IMPLEMENTAÇÃO
O primeiro passo, é procedermos a modelagem ORM da nova entidade (cliente) e realizar o mapeamento bi-direcional entre cliente e produto. Assim, temos:
Cliente:
package br.com.serjava.persistencia.entity; //imports omitidos /** * The persistent class for the cliente database table. * */ @Entity public class Cliente implements Serializable { private static final long serialVersionUID = 1L; @Id @SequenceGenerator(name="CLIENTE_IDCLIENTE_GENERATOR", sequenceName="CLIENTE_ID_CLIENTE_SEQ", allocationSize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="CLIENTE_IDCLIENTE_GENERATOR") @Column(name="id_cliente") private Integer idCliente; private String nome; //bi-directional many-to-many association to Produto @ManyToMany @JoinTable( name="cliente_produto_rel" , joinColumns={ @JoinColumn(name="id_cliente") } , inverseJoinColumns={ @JoinColumn(name="id_produto") } ) private ListProduto:listaProdutos; //getters and setters
package br.com.serjava.persistencia.entity; //imports omitidos /** * The persistent class for the produto database table. * */ @Entity public class Produto implements Serializable { private static final long serialVersionUID = 1L; @Id @SequenceGenerator(name="PRODUTO_IDPRODUTO_GENERATOR", sequenceName="PRODUTO_ID_PRODUTO_SEQ") @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="PRODUTO_IDPRODUTO_GENERATOR") @Column(name="id_produto") private Integer idProduto; @Column(name="nm_produto") private String nomeProduto; private Integer quantidade; private Double valor; //bi-directional many-to-many association to Cliente @ManyToMany(mappedBy="listaProdutos") private ListObserve que ambas entidades, Cliente e Produto, possuem uma List referenciando-se uma a outra, o que caracteriza o relacionamento N:N. No mapeamento da entidade Cliente, observe que o join que interliga as 2 entidades por meio da entidade rel, é realizado por meio da anotação própria: @JoinTable.listaClientes; //bi-directional many-to-one association to Categoria @ManyToOne @JoinColumn(name="id_categoria") private Categoria categoria;
Devemos criar a camada de persistência da nova entidade, Cliente. Faça de forma análoga as demais entidades (em caso de dúvidas consulte os posts anteriores sobre JPA).
Considerando que já temos produtos persistidos no database, vamos recuperá-los e associálos a um novo cliente que supostamente está visitando aqueles produtos em uma página; como o método save implementado no dao genérico (DAOImpl) retorna o objeto persistido (cliente), conseguiremos também obter os produtos associados ao cliente, por meio da relação, como mostra a implementação:
ProdutoDAO produtoDAO = new ProdutoDAOImpl(); ClienteDAO clienteDAO = new ClienteDAOImpl(); //obtem-se a lista de produtos salvos List<Produto> listaProdutosVisitados = produtoDAO.getAll(Produto.class); Cliente cliente = new Cliente(); //atribuição dos produtos a um cliente cliente.setListaProdutos(listaProdutosVisitados); cliente.setNome("Fábio"); //salva e obtém um cliente sincronizado com o BD Cliente clienteSalvo = clienteDAO.save(cliente); System.out.println("Nome cliente: " + clienteSalvo.getNome()); System.out.println("Produtos visitados (associados) ao cliente:"); //percorre a lista de produtos associados ao cliente salvo for (Produto produto : cliente.getProdutos()) { System.out.println(produto.getNomeProduto()); }
Observe que a relação no banco dá-se mediante a tabela de "rel" entre cliente e produtos. Mas em qual momento fizemos a relação dos identificados das entidades Cliente e Produto? A resposta é: quando atribuímos a lista de produtos ao objeto de cliente, e o JPA realizou todo o trabalho "braçal" pra gente. Veja a tabela associativa como ficou:
Neste exemplo, observe que temos 3 produtos com os ids: 16, 22 e 23. Lembre-se que recuperamos TODOS os produtos para, em seguida, associá-los ao cliente. O cliente Fábio tem o id 4. Logo, se os 3 produtos estão associados ao cliente 3, na tabela de rel, vemos tal relação contemplada!
Observe quanto trabalho o JPA lhe poupou! Espero que tenham gostado!
Muito bom!! parabéns!!!
ResponderExcluirmas e se eu tivesse um ou mais atributos nessa tabela cliente_produto_rel... como ficaria isso?
Olá Thiago, que bom que gostou.
ExcluirIdeia legal pra um post.
Caso tenha atributos que nao constituem a PK, ai você terá uma classe que será mapeada como @EmbeddedId na entidade principal.
vlw...
Excluirquando puder faz um tópico sobre isso!
Ola Fábio otimo exemplo, estou tentando buscar as informacoes de listaProdutos, para mostrar quantos produtos um cliente tem, mais não está funcionando.
ResponderExcluirMuito obrigado
List cliProd= cliente.getListaProdutos();
Excluirfor(Produto p:cliProd)
p.toString();
Da erro no toString dizendo que encontrou parametros vazios
Muito obrigado pela dica. Estava fazendo invertido o ManyToMany e não estava salvando os ID's na tabela relacionada. Agora deu certo!!
ResponderExcluirEste comentário foi removido pelo autor.
ResponderExcluirParabéns pelo blog, estava com um problema aqui no relacionamento ManyToMany e consegui resolver com a ajuda desse post. Obrigado e abraçoo
ResponderExcluirObrigado Bruno! Volte sempre
ExcluirMuito boa a explicação, mas no caso de um relacionamento no qual a tabela intermediária (na qual estão as chaves estrangeiras) possui um atributo comum as duas classes, como mapear isso no hibernate?
ResponderExcluir