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!

sexta-feira, março 16, 2012

Java One 2011 Presentation

Olá SeresJava!

Achei prudente divulgar, a Oracle disponibilizou os slides do Java One 2011, vale a pena conferir:

segunda-feira, março 12, 2012

Auditoria: Hibernate Envers

Olá seguidores e entusiastas do SerJava!!

Faz certo tempo que não os alimento de conteúdo, mas tenham certeza que não abandonei o blog nem o interesse em disseminar p conhecimento e, acima de tudo, propagar a tecnologia Java.

Desde que comecei a trabalhar no ecossistema Java, confesso, sempre tive uma queda pelas tecnologias relativas a ORM (Mapeamento Objeto-Relacional); gostos a parte, optei como artigo de retomada, abordar sobre o HibernateEnvers.

Comumente, desde pequenas a grandes aplicações, tem-se o desejo (necessidade realmente) de monitorar as ações do usuário frente ao sistema, de modo a prover controle sobre o uso da aplicação, bem como, resguardar a integridade das informações administradas pela mesma. Por certo, a maioria das aplicações desenvolvidas sobre a plataforma Java, fazem uso de frameworks ORM. Aproveitando os preceitos desta tecnologia, diga-se de passagem, já abordados em tópicos anteriores (mas ainda há mais conteúdos, aguardem!), o Hibernate dispões do subprojeto Envers.

O Hibernate Envers "provê o histórico de versionamento dos dados manipulados pela aplicação", ou seja, é possível fazer uso de objetos persistentes (entidades mapeadas para a persistência JPA) para auditar as modificações havidas num dado registro. O Envers, utiliza de artifícios de revisões, conceito similar ao subservion. Sua configuração é bastante simples, bastando a adição do jar correspondente ao classpath e a configuração do arquivo persistence.xml.

No intuito de ser objetivo, neste tópico vou abordar apenas os primeiros passos, novos posts trarão mais detalhes sobre o Envers.

Implementação

Aproveitando o conteúdo já trabalhado, a respeito de persistência nos posts anteriores, farei uso do projeto criado até então, onde foi realizado o mapeamento para a persistência de objetos representativos de um Produto (http://www.serjava.blogspot.com/2011/12/persistencia-jpa-dao-generico.html).

Como o projeto foi feito há um tempinho (rsrs) resolvi modernizar inserindo a última versão do Hibernate, a 4.0, disponível em: Hibernate Download, feito o download, observe que o diretório lib contém: envers, jpa,  required e optional. Para atualizar seu hibernate, delete todos os artefatos do seu projeto, exceto o driver de conexão com o banco (caso esteja utilizando o projeto iniciado anteriormente) e adicione os jars dos diretórios: envers, jpa e required no classpath da sua aplicação (o primeiro post sobre JPA mostra como fazer). Seu classpath, neste momento, deverá conter:


O próximo passo é adicionarmos a configuração do Envers, faremos a auditória, até então, apenas para a criação e atualização de dados, assim, seu arquivo persistence.xml, deverá ficar similar a este:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
 xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
 <persistence-unit name="PersistenciaPU"
  transaction-type="RESOURCE_LOCAL">

  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <class>br.com.serjava.persistencia.entity.Produto</class>

  <properties>
   <property name="hibernate.connection.username" value="postgres" />
   <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
   <property name="hibernate.connection.password" value="123456" />
   <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/PERSISTENCIA" />
   <property name="hibernate.show_sql" value="true" />
   
   <!-- Permite operacoes ddls pelo jpa -->
   <property name="hibernate.hbm2ddl.auto" value="update" />

   <!-- configuracao do Envers -->
   <property name="post-insert" value="org.hibernate.ejb.event.EJB3PostInsertEventListener, org.hibernate.envers.event.EnversListener" />
   <property name="post-update" value="org.hibernate.ejb.event.EJB3PostUpdateEventListener, org.hibernate.envers.event.EnversListener" />
  </properties>

 </persistence-unit>
</persistence>



O próximo passo, é marcarmos a entidade que deverá ser auditada, no caso Produto, apenas anotando-a com @Audited:
package br.com.serjava.persistencia.entity;
//import omitidos
@Entity
@Audited
@AuditTable(value="PRODUTO_AUDIT")
@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
}

A anotação @Audited diz ao Envers que esta entidade deverá ser auditada, e @AuditTable, discrimina, explicitamente, o nome da tabela a ser gerada que deverá conter as revisions dos dados.

Feito isto, execute a classe main, já implementado do projeto anterior, em seguida, acesso o banco postgres, e verifique a criação das tabelas: produto_audit e revinfo:



Pronto! Sua entidade produto já está sendo auditada, a pártir de agora, nas operações de inserção e atualização. Faça mais testes, com alterações. Nos próximos posts, demonstrarei como as revisions são organizadas e, como recuperar as revisões de um registro.

Espero que tenham gostado!