Páginas

Mostrando postagens com marcador ORM. Mostrar todas as postagens
Mostrando postagens com marcador ORM. Mostrar todas as postagens

quinta-feira, março 29, 2012

Persistência - JPA: OneToMany e ManyToOne


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 namereferencedColumnName 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!

terça-feira, dezembro 06, 2011

Persistência - JPA: Primeiros passos

Olá pessoAll!

Neste tópico abordarei a configuração e os primeiros passos para se usar a persistência de dados com um framework Objeto-Relacional (ORM).

Observo que muitas pessoas, principalmente iniciantes em desenvolvimento, têm grande interesse por tal aprendizado, porém, num primeiro momento encontram dificuldades que realmente tornam-se impactantes ao estudo e, consequentemente, ao aprendizado.

CONTEXTO
A grande motivação para o uso de um framework ORM em uma aplicação deu-se, mediante ao seguinte paradigma: de um lado encontra-se o banco de dados no SGBD (calcado suas bases na álgebra e o cálculo relacional) e de outro, o modelo da aplicação orientado a objetos, onde, ambos, representam, e uma forma ou de outra, a estrutura da aplicação. Neste sentido, deveria ser fácil realizar tal conversão: OO to Relational.

Embora, em tese, seria fácil tal conversão, verificou-se significativo trabalho e custo (tempo) para transformar um Modelo Orientado a Objetos, por meio e seu DAO (Data Access Object), utilizando-se JDBC.

Visando atender a esta necessidade, bem como, prover maior produtividade no desenvolvimento, surgiram-se várias propostas para a realização do mapeamento objeto-relacional, tais como: Hibernate, TopLink, EclipseLink, e outros. Neste cenário, cada proposta, evidentemente, trazia consigo suas especificidades no quesito implementação.

No intuito de se padronizar a implementação de tecnologias ORM, surgiu a JPA (Java Persistence API), que especificou o uso de ferramentas ORM. Em teoria, qualquer framework que implemente o padrão JPA deve funcionar igualmente.

MÃO NA MASSA
Será utilizado o SGBD PostgreSql para a a implementação.
Baseado no DER da tabela produto, exibido pela Figura 1:
Tem-se o script de criação da tabela, no SGBD:
CREATE TABLE produto
(
  id_produto serial NOT NULL,
  nm_produto character varying(45),
  quantidade integer,
  valor numeric,
  primary key produto_pkey PRIMARY KEY (id_produto)
)

Criaremos um projeto intitulado como Persistencia, do tipo Java Project no Eclipse. Posteriormente, devemos acrescer a característica de um projeto JPA, para tanto, clique com o botão direito sobre o projeto criado, e selecione a opção Properties.
Em seguida, seleciona na barra lateral, a opção Project Facets, e clique no link Convert do Faceted Form, e marque a opção JPA, conforme a Figura 2:

Observe que logo abaixo do quadro de opções, apareceu um alerta, "further configuration required", isto acontece pois o Eclipse não contém os artefatos necessários para serem adicionados ao classpath da aplicação. Para resolvermos isto, e colocarmos manualmente todos os artefados, clique sobre o link desta mensagem, e no combo Type, selecione Disable Library Configuration, conforme Figura 3:


Em seguida, clique em Ok, e o projeto ja deverá estar pronto para procedemos a configuração do JPA. Devemos, num primeiro momento, adicionarmos os artefatos necessários a isto, assim, na mesma hierarquia do diretório src do projeto, criaremos o diretório libs.

Tendo criado o diretório, adicionaremos os artefatos necessários, identifique-os na Figura 4:


Para a obtenção destes artefatos, acesse o sites: http://commons.apache.org/http://www.hibernate.org/ , http://www.slf4j.org/http://jdbc.postgresql.org/, ou então, como uma simples busca pela nomenclatura de cada arquivo.

Uma vez adicionados os artefatos necessários, resta referenciá-los aos classpath da aplicação, para isso, selecione todos eles, clique com o botão direito sobre, e siga as opções: Build Path --> Add to build path. Pronto, todas as dependências estão devidamente adicionadas e referenciadas.

O arquivo persistence.xml, contido em META-INF, dentro de src, deverá conter os dados necessários para conexão, devemos parametrizá-lo do seguinte modo:

 
 
  org.hibernate.ejb.HibernatePersistence
  
  
   
   
   
   
  
 
 


Observe que os dados de acesso ao banco foram parametrizados, tais como, url para acesso, usuário e senha (atenção, estes dados podem variar no seu ambiente).

CONEXÃO E MAPEAMENTO

A conexão com o banco de dados é gerida pelo EntityManager, da própria especificação Java Persistence API, este contém os métodos necessários para que o ORM realize suas operações de forma adequada.

Devemos criar um objeto Java que representará a estrutura da tabela produto que criamos no banco. Nesta classe, efetuaremos o mapeamento de modo que o JPA consiga entender o que o objeto e seus atributos representam no modelo relacional, assim segue-se:
package br.com.serjava.persistencia.entity;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name="produto")
@SequenceGenerator(name="produto_id_produto_seq", sequenceName="produto_id_produto_seq", allocationSize=1)
public class Produto implements Serializable {

 @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;
        //getters and setters



Criaremos agora a classe de conexão, que deverá gerar o EntityManager, portanto:
public class Conexao {

 //nome da unidade de persistencia definia no persistence.xml
 private static final String UNIT_NAME = "PersistenciaPU";
 
 private EntityManagerFactory emf = null;
 
 private EntityManager em = null;
 
 public EntityManager getEntityManager() {
  
  if (emf == null) {
   emf = Persistence.createEntityManagerFactory(UNIT_NAME);
  }
  
  if (em == null) {
   em = emf.createEntityManager();
  }
  
  return em;
 }

Demonstrarei, como persistir um objeto de produto gerido pelo contexto de persistência do JPA, em posts futuros, abordarei as demais possibilidades e operações do JPA. Assim, necessitamos obter o EntityManager, e por ele, persistir o objeto Produto, que foi devidamente mapeado:
public class Main {

 public static void main(String... args) {
  
  Produto produto = new Produto();
  produto.setNomeProduto("produto");
  produto.setQuantidade(22);
  produto.setValor(33.99);
  
  EntityManager em = new Conexao().getEntityManager();
  
  em.getTransaction().begin();
  em.persist(produto);
  em.getTransaction().commit();
 }
}

Execute a classe Main, e verifique no banco que deverá haver um registro com as informações atribuidas ao objeto Produto.

Espero que tenham gostado, tentei resumir um vasto e detalhado processo da forma mais simples e objetiva possível, espero ter atendido as expectativas.
Até breve!