Páginas

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!

quarta-feira, dezembro 07, 2011

Persistência - JPA: DAO Genérico

Olá Seres Java!

Dando continuidade ao post anterior, demonstrarei o emprego do padrão DAO para realizar a persistência por meio da especificação JPA. O uso do pattern DAO, além de prover um melhor design para aplicação, agregar princípios de boas práticas, propicia um melhor reaproveitamento de implementação.

Segundo o Core J2EE Patterns, um Data Access Object:
Access to data varies depending on the source of the data. Access to persistent storage, such as to a database, varies greatly depending on the type of storage (relational databases, object-oriented databases, flat files, and so forth) and the vendor implementation.
Desta forma, entende-se que emprego de um DAO na aplicação propicia um ponto central, para acesso a dados, fazendo uso de um quantitativo de classes relativamente pequenos.

Tomando como base o projeto implementado no post anterior, será acrescida a estrutura de persistência por meio do DAO Genérico, assim, segue-se:

Criação da Interface DAO, esta deverá listar os métodos comuns a serem implementados por todos os DAOs:

Interface DAO:
package br.com.serjava.persistencia.dao;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;

public interface DAO<T, I extends Serializable> {

 public T save(T entity);
 
 public void remove(T entity);
 
 public T getById(Class<T> classe, I pk);
 
 public List<T> getAll(Class<T> classe);
 
 public EntityManager getEntityManager();
}


Uma vez concluído a interface DAO, criaremos uma classe abstrata (ou seja, só poderá ser extendida por outra, e não instanciada) denominada DAOImpl, esta conterá a implementação genérica dos métodos comuns:


DAOImpl:
package br.com.serjava.persistencia.dao.impl;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;

import br.com.serjava.persistencia.dao.DAO;
import br.com.serjava.persistencia.util.Conexao;

public abstract class DAOImpl<T, I extends Serializable> implements DAO<T, I>{

 private Conexao conexao;
 
 @Override
 public T save(T entity) {
  
  T saved = null;
  
  getEntityManager().getTransaction().begin();
  saved = getEntityManager().merge(entity);
  getEntityManager().getTransaction().commit();
  
  return saved;
 }

 @Override
 public void remove(T entity) {
  getEntityManager().getTransaction().begin();
  getEntityManager().remove(entity);
  getEntityManager().getTransaction().commit();
  
 }

 @Override
 public T getById(Class<T> classe, I pk) {
  
  try {
   return getEntityManager().find(classe, pk);
  } catch (NoResultException e) {
   return null;
  }
  
 }

 @SuppressWarnings("unchecked")
 @Override
 public List<T> getAll(Class<T> classe) {
  
  return getEntityManager().createQuery("select o from " + classe.getSimpleName() + " o").getResultList();
 }

 @Override
 public EntityManager getEntityManager() {
  
  if (conexao == null) {
   conexao = new Conexao();
  }
  return conexao.getEntityManager();
 }

}


Uma vez criada a implementação base, resta-nos criar a interface que descreverá os métodos de um DAO referente a produto, que é a nossa entidade mapeada pelo JPA, assim temos que:


ProdutoDAO:
package br.com.serjava.persistencia.dao;

import br.com.serjava.persistencia.entity.Produto;

public interface ProdutoDAO extends DAO<Produto, Integer> {

}

Observem que a interface para Produto, é um DAO, como pode ser observado, esta extends a interface DAO padrão. Neste momento a estrutura genérica criada é tipada, quando informou-se: [...] extends DAO<Produto..., em outras palavras, estamos dizendo ao compilador da JVM que os métodos básicos herdados do DAO por ProdutoDAO, aceitarão exclusivamente objetos de Produto, desta forma, em tempo de compilação, se acidentalmente um desenvolvedor passar como parâmetro um objeto que não seja de Produto, será lançado um erro de compilação.

E, finalmente, tem-se a implementação de ProdutoDAO:


ProdutoDAOImpl:
package br.com.serjava.persistencia.dao.impl;

import br.com.serjava.persistencia.dao.ProdutoDAO;
import br.com.serjava.persistencia.entity.Produto;

public class ProdutoDAOImpl extends DAOImpl<Produto, Integer> implements ProdutoDAO {

}

Feito isto, temos nossa estrutura de persistência genérica, e sua implementação para persistirmos um objeto de Produto, na classe Main, criada no post anterior, demonstrarei o processo para se salvar e listar os objetos salvos:

public class Main {

 public static void main(String... args) {
  
  
  Produto produto = new Produto();
  produto.setNomeProduto("produto");
  produto.setQuantidade(22);
  produto.setValor(33.99);
  
  ProdutoDAO produtoDAO = new ProdutoDAOImpl();
  
  //salva um produto
  produtoDAO.save(produto);
  
  List<Produto> listaProdutosCadastrados = produtoDAO.getAll(Produto.class);
  
  for (Produto p : listaProdutosCadastrados) {
   System.out.println(p.getNomeProduto());
  }

 }
}


Aconselho, utilizando-se o mesmo princípio, testarem os demais métodos genéricos por meio de produto (getById, remove).

Para uma melhor compreensão da estrutura do projeto, segue sua estrutura e divisão de pacotes:

Bom, por este post é isto! Espero que tenham gostado e visualizado a flexibilidade que uma implementação genérica propicia!

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!

sexta-feira, novembro 18, 2011

Spring Framework: primeiros passos

Olá pessoal!

Demonstrarei neste post, como seu próprio título diz, os primeiros passos para se trabalhar com o popular e usual SpringFramework.

Segundo o Guia de Referência do Spring, este é definido como: "Spring Framework is a Java platform that provides comprehensive infrastructure support for developing Java applications. Spring handles the infrastructure so you can focus on your application.".

De modo geral, o uso do Spring permite a construção de aplicações a pártir de POJOs, seus recursos aplicam-se totalmente ao JSE e parcialmente ao JEE; seu uso permite a construção ágil de aplicações, de modo a garantir uma melhor coesão e baixo acoplamento.

Apesar de ser possível o uso de anotações para sua manipulação, neste primeiro post, demonstrarei seu uso somente a partir das configurações nativas em XML.

Para tanto, como nos demais posts, usarei a IDE Eclipse. Criaremos um projeto JSE convencional, onde simularemos a chamada a um componente de negócio, e este, chamará um objeto de acesso a dados hipotético, de modo a demonstrar a injeção de dependência entre os beans gerenciados pelo Spring.

CONSTRUÇÃO DO PROJETO
Assumindo que o projeto já tenha sido devidamente criado, criaremos uma interface denominada de TesteComponent, a qual representa um componente de negócio hipotético, e uma classe que a implementa, sendo TesteComponentImpl; tal componente, deverá chamar uma ação hipotética de um DAO, que será injetado pelo contexto do spring. Seguem as implementações: (atentem-se aos pacotes criados nos quais cada implementação está contida):

TesteDAO:
package br.com.serjava.dao;

public interface TesteDAO {

 public void metodoDao();
}

TesteDAOImpl:
package br.com.serjava.dao.impl;

import br.com.serjava.dao.TesteDAO;

public class TesteDAOImpl implements TesteDAO {

 @Override
 public void metodoDao() {
  System.out.println("método de acesso a dados executado");
  
 }

}

TesteComponent:
package br.com.serjava.component;


public interface TesteComponent {
 
 public void metodoTeste();
}


TesteComponentImpl
package br.com.serjava.component.impl;

import br.com.serjava.component.TesteComponent;
import br.com.serjava.dao.TesteDAO;

public class TesteComponentImpl implements TesteComponent {

 private TesteDAO testeDAO;
 
 public TesteComponentImpl(TesteDAO testeDAO) {
  this.testeDAO = testeDAO;
 }
 
 
 @Override
 public void metodoTeste() {
  System.out.println("Método teste executado!");
  
  testeDAO.metodoDao();
  
 }

}

Para uma melhor compreensão da arquitetura utilizada pelo projeto, segue um print da estrutura da pacotes da IDE:
Figura 1 - Estrutura projeto
Inclusão do Spring
Para adicionarmos as funcionalidades do Spring necessitamos fazer download de seus artefatos, este podem ser obtidos em: http://www.springsource.com/download/community, neste post utilizei a versão 3.1.0M2.

Para a adição das dependências, criaremos um diretório denominado de lib, na mesma hierarquia do diretório src. Dentro de lib, colocaremos os jars do Spring e suas dependências que se fizerem necessárias, para este exemplo, apenas: commons-logging, que pode ser obtido em http://commons.apache.org/logging/. Observe os arquivos que devem ser adicionados:
Uma vez adicionados os arquivos listados acima, devemos adicioná-los aos classpath da aplicação, basta clicar sobre cada um com o botão direito --> BuildPath --> Add to BuildPah. Pronto, as dependências do Spring estão devidamente configuradas.

Para configurarmos as classes que criamos para serem gerenciadas pelo Spring, criaremos o arquivo applicationContext.xml, dentro de src (deverá ficar como mostrado na Figura 1), segue o conteúdo do arquivo:


 
 
 
  
  
  







Execução
Por fim, para executarmos o projeto e testarmos o Spring, criaremos uma classe denominada por Main, para fazer a chamada inicial:
package br.com.serjava.view;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import br.com.serjava.component.TesteComponent;

public class Main {

 public static void main(String... args) {
  
  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  
  TesteComponent testeComponent = (TesteComponent) context.getBean("testeComponent");
  
  testeComponent.metodoTeste();
 }
}

Observe que em nenhum momento, na implementação, instanciou-se algum objeto usando-se new, as instâncias deverão serem geradas e gerenciadas pelo Spring, baseadas na injeção de dependência.

Executado o projeto, o resultado deverá ser, no console da ide:


Método teste executado!
método de acesso a dados executado


Espero que tenham gostado do Spring, em breve novos posts com demais funcionalidades e sua configuração e uso em projetos Web.

segunda-feira, novembro 14, 2011

DWR: Trabalhando com objetos

Olá pessoal! 
Em primeiro momento, agradeço aqueles que têm acompanhado o blog e ao feedback positivo.

Dando continuidade do estudo do DWR Direct Web Remoting, demonstrarei o uso de objetos Java no código JavaScript. Para ilustrar o exemplo, utilizarei a estrutura do projeto iniciado no primeiro post de DWR (http://serjava.blogspot.com/2011/10/dwr-primeio-projeto.html), simulando o cadastro de Funcionários.

O primeiro passo é criarmos um objeto que contenha as características do funcionário, para tanto, utilizarei o padrão DTO – Data Transfer Object, criando então FuncionarioDTO:


Esta classe deverá conter os seguintes atributos:
package br.com.serjava.dto;

public class FuncionarioDTO {

 private Integer codigo;
 
 private String nome;
 
 private String cargo;
 
 private Double salario;

//getters and setters


O próximo passo, é mapear esta classe no arquivo dwr.xml, para isso basta adicionarmos a sequinte linha no referido arquivo:
<convert converter="bean" match="br.com.serjava.dto.FuncionarioDTO" javascript="Funcionario"/>


Uma vez tendo mapeado tal classe, permitimos que esta seja reconhecida dentro do context do DWR, no atributo javascript, informamos o nome de Funcionario, este será o nome para o acesso via JavaScript.

Criaremos a classe para tratar as chamadas Ajax referentes ao funcionário, chamaremos de FuncionarioAjax, conforme imagem:
Criaremos dois métodos nesta classe, um que será responsável pelo cadastro dos funcionários, e outro que deverá obter os funcionários cadastrados. Observe que foi criada uma lista de funcionários estática, para armazenar os funcionários. Evidentemente que esta, num ambiente real, deverá ser substituída pelo repositório de dados.

package br.com.serjava.dwr;

import java.util.ArrayList;
import java.util.List;

import br.com.serjava.dto.FuncionarioDTO;

public class FuncionarioAjax {

 private static List<FuncionarioDTO> listaFuncionarios = new ArrayList<FuncionarioDTO>();
 
 public void cadastrarFuncionario(FuncionarioDTO funcionarioDTO) {
  
  System.out.println("funcionariod: " + funcionarioDTO.toString());
  listaFuncionarios.add(funcionarioDTO);
 }
 
 public List<FuncionarioDTO> obterFuncionarios() {
    
  return listaFuncionarios;
 }
}



Devemos mapear a classe FuncionarioAjax no arquivo dwr.xml, de modo a poder ser acessada via função JavaScript, para tanto:
<create creator="new" javascript="FuncionarioAjax">
    <param name="class" value="br.com.serjava.dwr.FuncionarioAjax"/>
</create>
Criaremos uma página jsp para conter o cadastro destes funcionários, esta ficará na raiz de WebContent. 
Depois de criada a página, antes de mais nada, devemos adicionar os imports para uso posterior, o que inclui, as funções do DWR e a classe mapeada para receber as solicitações ajax, então deve-se adicionar, dentro da tag head:



Criaremos agora o esboço para o formulário de cadastro, dentro da tag body:


Código
Nome
Cargo
Salário
Observe que o botão cadastrar chama uma função javascript, esta será responsável por criar o objeto JavaScript que representa nosso objeto FuncionarioDTO, esta função foi implementada do seguinte modo, dentro da tag head:
function cadastrarFuncionario() {
   
   var funcionario = {
    codigo: dwr.util.getValue('idCodigo'),
    nome: dwr.util.getValue('idNome'),
    cargo: dwr.util.getValue('idCargo'),
    salario: dwr.util.getValue('idSalario')
   };
   
   FuncionarioAjax.cadastrarFuncionario(funcionario, {
    callback:function() {
     alert('funcionário cadastrado');
    }
   });
  }
Observe que a variável criada como funciona'rio, tem seus atributos nomeado de forma idêntica aqueles da classe FuncionarioDTO, usamos a função dwr.util.getValue, para obter o value de um elemento informado seu id, observe que os valores informados correspondem com os ids denominados para cada input; ela é similar a função document.getElementById.

Como pode ser observado, atribuímos os valores correspondentes a cada atributo do objeto funcionario, em seguida chamamos a função cadastrarFuncionario;

Para melhorar nosso modelo, a cada funcionário cadastrado, será exibido numa tabela abaixo do formulário de cadastro, para isso criaremos uma tabela do seguinte modo, abaixo do referido formulários:
Observe que não foi feita nenhuma implementação para esta tabela.

Criaremos uma função javaScript responsável por obter os dados para popular a tabela:
function carregarTabela() {
   
   var cellsFuncionario = [
                  function(funcionario) {return funcionario.codigo;},
                  function(funcionario) {return funcionario.nome;},
                  function(funcionario) {return funcionario.cargo;},
                  function(funcionario) {return funcionario.salario;},
                  ];
   
   FuncionarioAjax.obterFuncionarios({
    callback:function(listaFuncionarios) {
     dwr.util.removeAllRows('tableFuncionarios');
     
     dwr.util.addRows( "tableFuncionarios", listaFuncionarios, cellsFuncionario, {escapeHtml:false});
    }
   });
  }

Para que depois de cadastrado um funcionário a tabela seja atualizada, na função de cadastro, onde verifica-se o alert, remova-o e adicione uma chamada a esta função, devendo ficar do seguinte modo:
function cadastrarFuncionario() {
   
   var funcionario = {
    codigo: dwr.util.getValue('idCodigo'),
    nome: dwr.util.getValue('idNome'),
    cargo: dwr.util.getValue('idCargo'),
    salario: dwr.util.getValue('idSalario')
   };
   
   FuncionarioAjax.cadastrarFuncionario(funcionario, {
    callback:function() {
     carregarTabela();
    }
   });
  }

Por fim, o resultado esperado, depois de uma inclusão deverá ser similar a:
Espero que tenham gostado! Em breve postarei mais especificidade e recursos sobre o DWR.