Ola PessoAll!
Dando continuidade a temática de Persistência de dados, demonstrarei neste post, a implementação do relacionamento de cardinalidade 1:N (1 para N), o que implica o uso das annotations: @ManyToOne e @OneToMany.
Como já visto em posts anteriores, o uso de frameworks ORM agregam bastante facilidade e agilidade no desenvolvimento de aplicações; sabe-se que modelagens de dados propiciam a coexistência de sua estrutura, ou seja, priorizam (na maioria das vezes) o uso de relacionamentos, ou seja, objetos de banco não têm muita significância quando isolados.
Para a obtenção de informações coesas, bem como, significantes ao contexto de negócio, o uso de relacionamentos é imprescindível, desta forma, saber manipular os relacionamentos no contexto ORM é significativamente importante.
Segundo a documentação da especificação JEE 6 (http://docs.oracle.com/javaee/6/tutorial/doc/bnbqa.html#bnbqh), tem-se:
- One-to-many: An entity instance can be related to multiple instances of the other entities. A sales order, for example, can have multiple line items. In the order application, Order would have a one-to-many relationship with LineItem. One-to-many relationships use the javax.persistence.OneToMany annotation on the corresponding persistent property or field.
- Many-to-one: Multiple instances of an entity can be related to a single instance of the other entity. This multiplicity is the opposite of a one-to-many relationship. In the example just mentioned, the relationship to Order from the perspective ofLineItem is many-to-one. Many-to-one relationships use the javax.persistence.ManyToOne annotation on the corresponding persistent property or field.
Será utilizado o projeto Persistência constituídos nos posts anteriores para a implementação. Ao trabalho!
A MODELAGEM
No nosso banco de dados que abrigava o cadastro de produtos, criaremos a tabela Categoria e o devido relacionamento com produto, conforme modelagem:
O script para a criação da tabela Categoria e inclusão do relacionamento em Produto, segue:
create table categoria ( id_categoria serial, nome varchar(45) not null, descricao varchar(200), fg_ativo boolean, primary key (id_categoria)); alter table produto add column id_categoria integer; alter table produto add constraint categoria_id_categoria_fk foreign key (id_categoria) references categoria (id_categoria);
IMPLEMENTAÇÃO
Feitas as alterações procederemos ao mapeamento, que deverá ser constituído da seguinte forma:
Produto:
package br.com.serjava.persistencia.entity; //imports omitidos @Entity @Table(name="produto") @SequenceGenerator(name="produto_id_produto_seq", sequenceName="produto_id_produto_seq", allocationSize=1) public class Produto implements Serializable { private static final long serialVersionUID = -8121617132071401241L; @Id @GeneratedValue(generator="produto_id_produto_seq", strategy=GenerationType.SEQUENCE) @Column(name="id_produto") private Integer idProduto; @Column(name="nm_produto") private String nomeProduto; @Column(name="quantidade") private Integer quantidade; @Column(name="valor") private Double valor; //relacionamento com categoria @ManyToOne @JoinColumn(name="id_categoria", referencedColumnName="id_categoria") private Categoria category; //getters and setters
Categoria:
package br.com.serjava.persistencia.entity; //imports omitidos @Entity @Table(name="categoria") @SequenceGenerator(name="categoria_id_categoria_seq", sequenceName="categoria_id_categoria_seq", allocationSize=1) public class Categoria implements Serializable { private static final long serialVersionUID = -8765631845563878481L; @Id @GeneratedValue(generator="categoria_id_categoria_seq", strategy=GenerationType.SEQUENCE) @Column(name="id_categoria") private Long idCategoria; @Column(name="nome", nullable=false) private String nome; @Column(name="descricao") private String descricao; @Column(name="fg_ativo") private Boolean ativo; @OneToMany(mappedBy="categoria") private List<Produto> listaProdutos; //getters and setters
Observe que na entidade Produto temos um objeto de Categoria, pois, de acordo com o modelo elaborado, um produto tem uma categoria; já na entidade Categoria, note a existência de uma lista de Produtos, o que indica que uma categoria pode estar associada a N produtos.
Atenção! O atributo mappedBy presente na annotation @OneToMany, presente na entidade Categoria, deve conter a nomenclatura dada ao objeto que referencia-se a uma categoria na entidade Produto!
Na entidade Produto, temos a anotação @JoinColumn (você deve estar pensando: Nossa é aqui que o join é feito? SIM!), seus atributos name e referencedColumnName significam, respectivamente, o nome do atributo correspondente a foreign key de categoria na tabela produto (no banco de dados) e o nome da primary key representativa da tabela Categoria!
O próximo passo é a criação do objeto de acesso a dados (DAO) para a entidade Categoria. Felizmente nosso projeto está usando a arquitetura DAO Genérico, o que nos poupará bastante trabalho. A criação de CategoriaDAO e CategoriaDAOImpl deverá ser feita de forma análoga a Produto, para não ser repetitivo, verifique nos posts anteriores (em caso de dúvidas consulte: Persistência - DAO Genérico).
Para efetivarmos o relacionamento, criaremos 2 Produtos e os vincularemos a uma dada Categoria, na classe main:
public static void main(String... args) { ProdutoDAO produtoDAO = new ProdutoDAOImpl(); CategoriaDAO categoriaDAO = new CategoriaDAOImpl(); Categoria categoria = new Categoria(); categoria.setNome("Livros Informática"); categoria.setDescricao("Livros de desenvolvimento, banco de dados..."); categoria.setAtivo(true); //Categoria é salva no banco e recuperada - uso do merge, ou seja, tem-se o objeto categoria // sincronizado no contexto de persistência categoria = categoriaDAO.save(categoria); Produto produto = new Produto(); produto.setNomeProduto("Head First - Java"); produto.setQuantidade(11); produto.setValor(99.99); //atribui a cateogira Livros Informática ao produto produto.setCategoria(categoria); Produto produto2 = new Produto(); produto2.setNomeProduto("Head First - Servlets and JSP"); produto2.setQuantidade(55); produto2.setValor(128.99); //atribui a cateogira Livros Informática ao produto produto2.setCategoria(categoria); //salva-se os produtos produtoDAO.save(produto); produtoDAO.save(produto2); List<Produto> listaProdutosCadastrados = produtoDAO.getAll(Produto.class); for (Produto p : listaProdutosCadastrados) { System.out.println(p.getNomeProduto()); } }
O resultado esperado é a inserção de 2 produtos vínculados a categoria criada. Vejamos o resultado no banco:
Observe que foi criada a Categoria 3 - Livros Informática, e os produtos 22 e 23, vinculados a esta categoria, pelo código 3!
Espero que tenham gostado!
Boa tarde Fabio,
ResponderExcluirEstou tentando implementar esse seu exemplo em um "projeto" de estudos que estou realizando. O meu projeto é um sistema WEB onde eu tenho duas tabelas uma de CLIENTE e outra de ESCRITORIO no meu caso vários clientes podem pertencer a um mesmo escritório, porém o mesmo cliente não pode pertencer a mais de um escritório ao mesmo tempo. Bem o relacionamento na tabela ficou da seguinte forma, tenho uma FK na coluna idEscritorio na tabela de cliente apontando para a tabela de escritorio no campo chave idEscritorio, na minha clase coloquei as anotações conforme vc explicou ou seja na classe Cliente ficou assim:
@ManyToOne
@JoinColumn(name="idEscritorio",referencedColumnName = "idEscritorio")
private Escritorio codigoEscritorio;
na classe Escritorio ficou assim:
@OneToMany(mapped = idEscritorio")
private List listaClientes;
Porém ao executar o meu código logo quando eu faço a primeira pesquisa ele exibe o erro:
org.hibernate.annotationexception @column(s) not allowed on a @manytoone property: modelo.Cliente.codigoEscritorio
OBS.: Antes de implementar já tinha um registro na tabela de cliente relacionado a tabela de escritorio, estou realizando primeiro a pesquisa para depois tentar incluir, e o erro ocorre logo na pesquisa ...
Pode me ajudar com esse problema ??
Desde já agradeço.
Obrigado.
Abs.
Boa tarde!
ExcluirObrigado pela visita!
Faça o seguinte, substitua:
@OneToMany(mapped = idEscritorio")
private List listaClientes;
por
@OneToMany(mapped = codigoEscritorio")
private List listaClientes;
o mapped faz referencia ao nome do atributo (objeto) mapeado na outra entidade e não o nome do campo do BD.
Faça o teste, e poste o resultado ai ;)
[]s
Bom dia Fábio,
ExcluirPrimeiramente muito obrigado pela sua atenção e resposta, alterei para o que disse e realmente funcionou :D .. mto obrigado ...
Agora pintou um outro erro que queria ver se pode me ajudar, sem querer abusar ....
Na minha JSP de incluir cliente eu tenho o campo Cód. Escritório que está da seguinte forma:
${escritorio.sigla}
OBS.: coloquei o d no final de selectd e optiond, pois essa tag não é permitida no post.
Conforme seu exemplo na minha clase Cliente eu tenho o atributo
private Escritorio codigoEscritorio;
quando vou salvar ocorre o seguinte erro:
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'cliente' on field 'codigoEscritorio': rejected value [1]; codes [typeMismatch.cliente.codigoEscritorio,typeMismatch.codigoEscritorio,typeMismatch.modelo.Escritorio,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [cliente.codigoEscritorio,codigoEscritorio]; arguments []; default message [codigoEscritorio]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'modelo.Escritorio' for property 'codigoEscritorio'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [modelo.Escritorio] for property 'codigoEscritorio': no matching editors or conversion strategy found]
Eu estou utilizando Spring MVC ... Pelo o que entendi o spring não conseguiu mapear isso de alguma forma ... Você pode me tirar mais essa dúvida ?
Obrigado pela atenção.
Abs.
Boa tarde Fábio, estou tentando realizar um insert em cascata na minha aplicação, porém, quando executo o comando o JPA me retorna o seguinte erro: NO TRANSACTION IS CURRENTLY ACTIVE, sendo que ao realizar o comando sem ser em cascata o insert funciona perfeitamente.
ResponderExcluirO relacionamento das minhas classes estão assim:
Entidade Grupo:
@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "nrGrupo")
private List procedimentosSubgrupoList;
Entidade Subgrupo:
@ManyToOne
@JoinColumn(name = "nr_grupo", referencedColumnName = "nr_grupo")
private ProcedimentosGrupo nrGrupo;
Como posso resolver o problema?
Muito bom meu amigo, Parabéns!
ResponderExcluir