Páginas

sexta-feira, 11 de janeiro de 2013

Bind entre Formulário HTML e POPO




No artigo anterior expliquei como popular um POJO (plain old java object) automaticamente a partir do request. Nesse artigo explicarei como implementar isso em PHP (POPO = plain old PHP object).


Vamos ver um cenário típico:

Definição da Classe:


<?php
class Pessoa {
   private $id;
   private $nome;
   private $email;
   private $telefone;

   //getters e setters
}
?>

Formulário HTML para cadastro:


<form action="cadastrar.php" method="post">
      Nome: <input type="text" name="nome" /><br/>
      Email: <input type="text" name="email" /><br/>
      Telefone: <input type="text" name="telefone" /><br/>
      
      <input type="submit" value="Cadastrar" />
</form>

Arquivo cadastrar.php

<?php
require_once 'Pessoa.php';
$pessoa = new Pessoa();
$pessoa->setNome($_REQUEST['nome']);
$pessoa->setEmail($_REQUEST['email']);
$pessoa->setTelefone($_REQUEST['telefone']);
//salva a pessoa no DAO
?>

Esse processo não é produtivo e muito entediante, especialmente se possuir muitos campos. Podemos usar um excelente recurso do PHP para automatizar tarefas repetitivas: Reflection.

Vejamos como poderíamos melhorar o processo:

Arquivo cadastrar.php

<?php
//Função que popula um objeto qalquer com dados do request.

public function populateFromRequest($object) {
        $reflectionClass = new ReflectionClass(get_class($object));
        foreach ($_REQUEST as $name => $value) {
            try {
                $typeProperty = $reflectionClass->getProperty($name);
                $typeProperty->setAccessible(true);
                $typeProperty->setValue($object, $value);
            } catch (Exception $exc) {}
        }
    }


require_once 'Pessoa.php';
$pessoa = new Pessoa();
populateFromRequest($pessoa, 'Pessoa');

//salva a pessoa no DAO
?>

A função populateFromRequest procura no objeto as propriedades cujos nomes coincidam com os nomes dos campos guardados no $_REQUEST, quando os nomes batem o valor da propriedade é setada com o valor do campo no $_REQUEST.

Essa função deverá ser isolada pois pode ser usada em outras situações com objetos diferentes.

Atenciosamente,
Gustavo Marques


sexta-feira, 4 de janeiro de 2013

Caixas de Dialogo com Javascript



Quem nunca exibiu uma mensagem em javascript para o usuário com alert('Sucesso'); que jogue a primeira pedra! É rápido e prático, tenho que adimitir. Porém nesse artigo vou apresentar algo melhor: o componente Dialog da biblioteca JQueryUI.


Veja abaixo como funciona:



Bem mais atraente do que a caixa de dialog do alert, que difere entre os browsers.
Essa caixa ainda pode ser arrastada e redimencionada.

Veja como a implementação é fácil:


<html>
<head>       
   <!--IMPORTAÇÕES NECESSÁRIAS-->
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" />
    <script src="http://code.jquery.com/jquery-1.8.3.js"></script>
    <script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>        
</head>
<body>
<div id="dialog" title="Titulo da Caixa">
    <p>Ut a placerat diam. Suspendisse euismod nulla ac quam tristique eleifend iaculis dolor luctus. Aliquam tristique rhoncus placerat. Ut adipiscing lorem pharetra tortor viverra quis ornare mi pharetra. Maecenas a massa ante, a accumsan leo. Donec ac turpis et arcu tincidunt tincidunt. Etiam sed nunc nulla. Mauris posuere augue sapien.</p>
</div>
<script>
$( "#dialog" ).dialog();
</script>
</body>
</html>

O conteúdo da div com id='dialog' será o conteúdo do dialogo e o atributo title dessa div será o título do diálogo, bem simples. E o grande diferencial em relação ao tradicional alert(), é que podemos inserir HTML livremente dentro do Dialog, tornando assim nossos diálogos mais funcionais e estilizados. No exemplo dessa página eu coloquei um UL com itens LI, mas poderia ser uma imagem ou qualquer outra coisa.

Atenciosamente,
Gustavo Marques

Menus com Javascript

Seguindo com os artigos sobre interface rica para web, vamos analisar agora o componente menu da biblioteca JQueryUI.
Veja abaixo um menu feito (passe o mouse para expandi-lo)





É incrivelmente simples fazer um menu, basta trabalharmos com ULs e LIs aninhadas. Cada LI será um item do menu, quando houver necessidade de um sub menu, basta colocar um UL dentro do LI.

Segue o código:


<html>
<head>       
   <!--IMPORTAÇÕES NECESSÁRIAS-->
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" />
    <script src="http://code.jquery.com/jquery-1.8.3.js"></script>
    <script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>        
<style>
#menu{
width:200px;
}
#menu ul{
width:200px;
}
</style>
</head>
<body>


<ul id="menu" >
    <li class="ui-state-disabled"><a href="#">Desabilitado</a></li>
    <li><a href="#">Item</a></li>
    <li><a href="#">Item</a></li>
    <li><a href="#">Item</a></li>
    <li>
        <a href="#">Item</a>
        <ul>
            <li><a href="#">Item nivel 2</a></li>
            <li><a href="#">Item nivel 2</a></li>
            <li><a href="#">Item nivel 2</a></li>
        </ul>
    </li>
    <li><a href="#">Item</a></li>
    <li>
        <a href="#">Item</a>
        <ul>
            <li>
                <a href="#">Item nivel 2</a>
                <ul>
                    <li><a href="#">Item nivel 3</a></li>
                    <li><a href="#">Item nivel 3</a></li>
                    <li><a href="#">Item nivel 3</a></li>
                </ul>
            </li>
            <li>
                <a href="#">Item nivel 2</a>
                <ul>
                    <li><a href="#">Item nivel 3</a></li>
                    <li><a href="#">Item nivel 3</a></li>
                    <li><a href="#">Item nivel 3</a></li>
                </ul>
            </li>
            <li><a href="#">Item nivel 2</a></li>
        </ul>
    </li>
    <li class="ui-state-disabled"><a href="#">Desabilitado</a></li>
</ul>
<script>
$( "#menu" ).menu();
</script>

</body>
</html>


Coloque uma largura no menu para ele não ocupar a tela inteira.

Atenciosamente,
Gustavo Marques

Tabs com Javascript




Continuando nossa série de artigos sobre interface rica, vamos ver agora o componente Tabs, da JQueryUI.  Tabs são uma forma conveniente de dividir o conteúdo. São abas superiores que mostram um conteúdo por vez. Veja abaixo um exemplo (clique nas abas para mudar o conteúdo):


Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc id nibh sed metus blandit sagittis. Sed a bibendum turpis. Suspendisse scelerisque el.
Fusce ultrices, nisl quis egestas facilisis, lorem tortor consectetur nibh, sit amet fringilla ligula arcu ac lorem. Duis ut sem quam, ac placerat.
Duis aliquet felis eu elit dignissim fermentum. Nunc tellus erat, elementum ut fringilla quis, interdum vel purus. Duis a risus at nisi tristique.
Nullam convallis sollicitudin lorem, nec tempus diam porttitor fringilla. Etiam facilisis lacus sit amet erat lobortis ac faucibus nunc posuere.
Curabitur pharetra consectetur nibh ac consequat. Proin sed turpis quis sapien pellentesque luctus. In pulvinar dolor eu tellus lacinia sed vehicula lacus faucibus.



Implementação:
Para implementar veja o código:


<html>
<head>       
   <!--IMPORTAÇÕES NECESSÁRIAS-->
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" />
    <script src="http://code.jquery.com/jquery-1.8.3.js"></script>
    <script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>        
</head>
<body>
<div id="tabs">
    <ul>
        <li><a href="#tabs-1">Aba 1</a></li>
        <li><a href="#tabs-2">Aba 2</a></li>
        <li><a href="#tabs-3">Aba 3</a></li>
    </ul>
    <div id="tabs-1">
        <p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc id nibh sed metus blandit sagittis. Sed a bibendum turpis. Suspendisse scelerisque el.
</p>
    </div>
    <div id="tabs-2">
        <p>
Fusce ultrices, nisl quis egestas facilisis, lorem tortor consectetur nibh, sit amet fringilla ligula arcu ac lorem. Duis ut sem quam, ac placerat.
        </p>
<p>
Duis aliquet felis eu elit dignissim fermentum. Nunc tellus erat, elementum ut fringilla quis, interdum vel purus. Duis a risus at nisi tristique.
</p>
    </div>
    <div id="tabs-3">
        <p>
Nullam convallis sollicitudin lorem, nec tempus diam porttitor fringilla. Etiam facilisis lacus sit amet erat lobortis ac faucibus nunc posuere.
</p>
<p>
Curabitur pharetra consectetur nibh ac consequat. Proin sed turpis quis sapien pellentesque luctus. In pulvinar dolor eu tellus lacinia sed vehicula lacus faucibus.
</p>
    </div>
</div>
<script>
$( "#tabs" ).tabs();
</script>

</body>
</html>

As abas superiores são uma ul com os titulos. Já o conteúdo fica dentro das divs internas.
Então chame $('#tabs').tabs(); para criar o componente.

Atenciosamente,
Gustavo Marques

quinta-feira, 3 de janeiro de 2013

Datepicker com JQueryUI



No artigo anterior apresentei o Accordion. Agora vamos analisar um recurso bastante útil: DatePicker. Vez por outra nossos formulários possuem campos do tipo data para o usuário preencher. A data a ser informada precisa seguir um padrão, geralmente dd/mm/aaaa. Também é preciso validar datas incorretas, como 87/50/2980 (não existe dia 87, tão pouco mês 50). O Datepicker soluciona esses problemas. Ele apresenta um calendário para o usuário escolher a data. Veja um exemplo abaixo (clique na caixa de texto):


Informe a data de seu nascimento:


Assim, além de visualmente atraente, solucionamos os problemas de formatos errados.

Vejamos o código:

<input id="datapicker" type="text" />
<script>

$('#datapicker').datepicker();
$('#datapicker').datepicker( "option", "dateFormat", "dd/mm/yy" );

</script>


A linha $('#datapicker').datepicker(); usa o seleter por id do jquery para encontrar o campo data, e então aplica a função datepicker; Já a linha abaixo definimos o formato da data a ser exibida.

Não esqueça de incluir as bibliotecas necessárias dentro da tag <head>

<head> 
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" />
    <script src="http://code.jquery.com/jquery-1.8.3.js"></script>
    <script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>        
</head>

Bem simples.

Atenciosamente,
Gustavo Marques

Accordion com JqueryUI




A biblioteca JQueryUI fornece efeitos visuais interessantes, antes só possíveis com flash. Neste artigo vamos explorar o Accordion. Veja abaixo o que é um Accordion e como ele funciona (faça algumas interações com ele):


Titulo 1

Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográfica e de impressos, e vem sendo utilizado desde o século XVI, quando um impressor desconhecido pegou uma bandeja de tipos e os embaralhou para fazer um livro de modelos de tipos. Lorem Ipsum sobreviveu não só a cinco séculos, como também ao salto para a editoração eletrônica, permanecendo essencialmente inalterado. Se popularizou na década de 60, quando a Letraset lançou decalques contendo passagens de Lorem Ipsum, e mais recentemente quando passou a ser integrado a softwares de editoração eletrônica como Aldus PageMaker.

Titulo 2

Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográfica e de impressos, e vem sendo utilizado desde o século XVI, quando um impressor desconhecido pegou uma bandeja de tipos e os embaralhou para fazer um livro de modelos de tipos. Lorem Ipsum sobreviveu não só a cinco séculos, como também ao salto para a editoração eletrônica, permanecendo essencialmente inalterado. Se popularizou na década de 60, quando a Letraset lançou decalques contendo passagens de Lorem Ipsum, e mais recentemente quando passou a ser integrado a softwares de editoração eletrônica como Aldus PageMaker.

Titulo 3

Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográfica e de impressos, e vem sendo utilizado desde o século XVI, quando um impressor desconhecido pegou uma bandeja de tipos e os embaralhou para fazer um livro de modelos de tipos. Lorem Ipsum sobreviveu não só a cinco séculos, como também ao salto para a editoração eletrônica, permanecendo essencialmente inalterado. Se popularizou na década de 60, quando a Letraset lançou decalques contendo passagens de Lorem Ipsum, e mais recentemente quando passou a ser integrado a softwares de editoração eletrônica como Aldus PageMaker.

Titulo 4

Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográfica e de impressos, e vem sendo utilizado desde o século XVI, quando um impressor desconhecido pegou uma bandeja de tipos e os embaralhou para fazer um livro de modelos de tipos. Lorem Ipsum sobreviveu não só a cinco séculos, como também ao salto para a editoração eletrônica, permanecendo essencialmente inalterado. Se popularizou na década de 60, quando a Letraset lançou decalques contendo passagens de Lorem Ipsum, e mais recentemente quando passou a ser integrado a softwares de editoração eletrônica como Aldus PageMaker.


O Accordion permite a divisão do conteúdo em abas horizontais, de modo que apenas uma sessão esteja disponível por vez.

Para implementar, use o código:


<html>
<head>       

   <!--IMPORTAÇÕES NECESSÁRIAS-->
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" />
    <script src="http://code.jquery.com/jquery-1.8.3.js"></script>
    <script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>        
</head>
<body>
<div id="accordion">

<h3>Titulo 1</h3>
<div>
Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográfica e de impressos, e vem sendo utilizado desde o século XVI, quando um impressor desconhecido pegou uma bandeja de tipos e os embaralhou para fazer um livro de modelos de tipos. Lorem Ipsum sobreviveu não só a cinco séculos, como também ao salto para a editoração eletrônica, permanecendo essencialmente inalterado. Se popularizou na década de 60, quando a Letraset lançou decalques contendo passagens de Lorem Ipsum, e mais recentemente quando passou a ser integrado a softwares de editoração eletrônica como Aldus PageMaker.
</div>

<h3>Titulo 2</h3>
<div>
Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográfica e de impressos, e vem sendo utilizado desde o século XVI, quando um impressor desconhecido pegou uma bandeja de tipos e os embaralhou para fazer um livro de modelos de tipos. Lorem Ipsum sobreviveu não só a cinco séculos, como também ao salto para a editoração eletrônica, permanecendo essencialmente inalterado. Se popularizou na década de 60, quando a Letraset lançou decalques contendo passagens de Lorem Ipsum, e mais recentemente quando passou a ser integrado a softwares de editoração eletrônica como Aldus PageMaker.
</div>

<h3>Titulo 3</h3>
<div>
Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográfica e de impressos, e vem sendo utilizado desde o século XVI, quando um impressor desconhecido pegou uma bandeja de tipos e os embaralhou para fazer um livro de modelos de tipos. Lorem Ipsum sobreviveu não só a cinco séculos, como também ao salto para a editoração eletrônica, permanecendo essencialmente inalterado. Se popularizou na década de 60, quando a Letraset lançou decalques contendo passagens de Lorem Ipsum, e mais recentemente quando passou a ser integrado a softwares de editoração eletrônica como Aldus PageMaker.
</div>

<h3>Titulo 4</h3>
<div>
Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográfica e de impressos, e vem sendo utilizado desde o século XVI, quando um impressor desconhecido pegou uma bandeja de tipos e os embaralhou para fazer um livro de modelos de tipos. Lorem Ipsum sobreviveu não só a cinco séculos, como também ao salto para a editoração eletrônica, permanecendo essencialmente inalterado. Se popularizou na década de 60, quando a Letraset lançou decalques contendo passagens de Lorem Ipsum, e mais recentemente quando passou a ser integrado a softwares de editoração eletrônica como Aldus PageMaker.
</div>

</div>

<script>
$('#accordion').accordion();
</script>

</body>
</html>

A linha $('#accordion').accordion(); usa o seletor por id do jquery.
Então use a estrutura acima para montar o accordion.

No próximo artigo analisaremos a útil função Datepicker para trabalhar com campos tipo data.

Atenciosamente,
Gustavo Marques

Bind entre Formulário HTML e POJO




Assumindo um cenário sem frameworks MVC, sempre que vamos cadastrar ou alterar algum POJO (Plain Old Java Object) em um Servlet é a mesma história: precisamos construir primeiro o objeto atribuindo a suas propriedades os valores vindos em uma requisição HTTP através de request.getParameter("campo"), para depois então passar o objeto construído para o DAO. 

Por exemplo, veja o código abaixo:

POJO

public class Pessoa {
   private Integer id;
   private String nome;
   private String email;
   private String telefone;
   private String endereco;
   private String numero;
   private String bairro;
   private String cidade;
   private String estado;
   //getters e setters
}

Form
<form action="CadastrarPessoa" method="post">
      Nome: <input type="text" name="nome" /><br/>
      Email: <input type="text" name="email" /><br/>
      Telefone: <input type="text" name="telefone" /><br/>
      Endereço: <input type="text" name="endereco" /><br/>
      Numero: <input type="text" name="numero" /><br/>
      Bairro: <input type="text" name="bairro" /><br/>
      Cidade: <input type="text" name="cidade" /><br/>
      Estado: <input type="text" name="estado" /><br/>
      <input type="submit" value="Cadastrar" />
</form>

Servlet

protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Pessoa pessoa = new Pessoa();
        pessoa.setNome(request.getParameter("nome"));
        pessoa.setEmail(request.getParameter("email"));
        pessoa.setTelefone(request.getParameter("telefone"));
        pessoa.setEndereco(request.getParameter("endereco"));
        pessoa.setNumero(request.getParameter("numero"));
        pessoa.setBairro(request.getParameter("bairro"));
        pessoa.setCidade(request.getParameter("cidade"));
        pessoa.setEstadol(request.getParameter("estado"));

       new PessoaDAO().cadastrar(pessoa);
}

Isso é realmente entediante, ainda mais se o POJO tiver vários campos.

E se houvesse uma maneira de preencher automaticamente um POJO com os dados do request? Uma maneira sem que precisássemos definir propriedade a propriedade? A biblioteca commons-beanutils da apache simplifica o trabalho com reflection e possui métodos convenientes para esse trabalho. O método que estamos falando é populate() da classe BeanUtils. Ele além de popular um beans também faz conversão automatica de tipo de dado. O jar commons-beanutils é um jar de dependencia em muitos frameworks.

Vamos criar uma classe auxiliar com um método estático que irá fazer esse trabalho.

public class Populate{
   public static void populate(Object pojo, HttpServletRequest request){
      BeanUtils.populate(pojo, request.getParameterMap());
   }
}

O método request.getParameterMap(); retorna um mapa com os pares nome da propriedade-valor. Exatamente o que o método populate de BeanUtils precisa para popular um bean. Porem para funcionar adequadamente, é preciso que o nome do campo do form HTML seja igual ao da propriedade do POJO.

Modificando então o nosso Servlet, vamos ter o código


protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Pessoa pessoa = new Pessoa();
        Populate.populate(pessoa, request);
        new PessoaDAO().cadastrar(pessoa);
}

Bem menos código não? E essa classe Populate ainda irá servir para contruir outros POJOs com campos completamente diferentes.

Atenciosamente,
Gustavo Marques


Um pequeno framework para JDBC




Nesse artigo irei apresentar um pequeno framework que irá adicionar funcionalidades extras ao JDBC, mais especificamente ao tradicional PreparedStatement do pacote java.sql. As funcionalidades extras são:

1- Conversão automática de um ResultSet para uma lista de objetos de negócio, eliminando assim a necessidade de manipular diretamente um ResultSet.

2- Cache opcional para buscas frequentes.

Vamos ver um exemplo de uso desse framework:

Tabela


CREATE TABLE `usuario` (
   `cod_usuario` int(11) NOT NULL AUTO_INCREMENT,
   `nome_usuario` text NOT NULL,
   `email_usuario` text NOT NULL,
   PRIMARY KEY (`id`)
)ENGINE=InnoDB;


Classe do dominio do negócio:


class Usuario{
private int id;
private String nome;
private String email;
//getters e setters
}

Dao usando o framework

Conversão


//dao
public List<Usuario> buscarPeloNome(java.sql.Connection con, String busca){
return new PreparedStatementPlus(con)
.prepareStatement("select cod_usuario as id from usuarios where nome_usuario = ?")
.setString(1, busca)
.executeQuery(Usuario.class);
}

Chemei ele de PreparedStatementPlus, devido as funções extras.

Onde está o resultSet? Vamos entender o código acima: O método buscarPeloNome() retorna uma lista de objetos Usuario. O método executeQuery() de um PreparedStatement tradicional iria retornar um ResultSet, mas o método executeQuery() do PreparedStatementPlus aceita um argumento informando o tipo de classe de retorno. Assim esse método, ao invés de retornar um ResultSet para que nós manualmente criemos os objetos, retorna logo a lista de objetos desejada. O mapeamento objeto-relacional é feito via reflection, o nome da coluna deve ser igual ao nome da propriedade da classe para que seja feita a atribuição. No caso do nome da coluna ser diferente, pode-se definir um apelido (ex. select cod_usuario as id from usuarios) para que os nomes casem.

Cache


Cada instancia do PreparedStatementPlus tem um cache interno para evitar consultas repetidas. Por exemplo:

PreparedStatementPluss p = new PreparedStatementPlus(con);//autoComit é setado para false a fim de se obter a proteção do banco quanto ao ACID
for(int i = 0; i < 1000; i++){
p.prepareStatement("select * from usuarios")
       
        //A LINHA ABAIXO CONSULTA O BANCO SÓ UMA VEZ, DURANTE O PRIMEIRO LOOP
        //NOS OUTROS LOOPS O FRAMEWORK DETECTARÁ QUE SE TRATA DA MESMA      
        //CONSULTA E IRÁ BUSCAR O RESULTADO NO CACHE
int total = p.executeQueryAndCacheResult(Usuario.class).size();

System.out.println(total+" usuarios encontrados");
}

Como há multiplas consultas para a mesma instancia do preparedStatementPlus este se utiliza de cache para consultas repetidas. Mas o seguinte codigo não guarda em cache

for(int i = 0; i < 1000; i++){
        //para cada loop um novo preparedStatementPlus é criado, 
        //inviabilizando assim o cache automático
PreparedStatementPluss p = new PreparedStatementPlus(con);
p.prepareStatement("select * from usuarios")
int total = p.executeQueryAndCacheResult(Usuario.class).size();
System.out.println(total+" usuarios encontrados");
}

Pode-se também transformar o cache de primeiro nível do PreparedStatementPlus em cache de segundo nível por guardá-lo em algum lugar. Ex. Guardando o cache em uma sessão JEE

//Servlet
PreparedStatementPluss p = new PreparedStatementPlus(con);
p.prepareStatement("select * from usuarios")
List<Usuario> usuarios = p.executeQueryAndCacheResult(Usuario.class);
//os usuarios são guardados em cache na sessão 
request.getSession().setAttribute("cache", p.getCache());

//em um outro servlet
PreparedStatementPluss p = new PreparedStatementPlus(con);

//faz o PreparedStatementPlus ler o cache informado
p.setCache((java.util.Map)request.getSession().getAttribute("cache"));
p.prepareStatement("select * from usuarios")

//não vai ao banco, pois esta consulta já esta no cache
List<Usuario> usuarios = p.executeQueryAndCacheResult(Usuario.class);


Naturalmente para este tipo de cache vocês mesmos deverão cuidar do ACID no banco, por exemplo ao inserir, editar ou deleter um usuario chame request.getSession().setAttribute("cache", null); para invalidar o cache, pois perdeu a sincronia com o banco

Deploy

O preparedStatement precisa do commons-beanutils.jar no classpath para funcionar

Código

Segue abaixo o código completo da classe:



import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;

/**
 * Serviços: 
 * Conversão de resultado para List de objeto
 * Cache de consultas
 * @author Gustavo
 */
public class PreparedStatementPlus {
    private String sqlquery;
    private StringBuilder parametrosString = new StringBuilder();
    private PreparedStatement p;
    private Connection con;
    
    /*
     * Cache de primeiro nível e/ou segundo 
     */
    private Map cache;

    public PreparedStatementPlus(Connection con) throws Exception{
        con.setAutoCommit(false);
        this.con = con;
    }

    /*
     * Método genérico, retorno lista de objetos encontrados na consulta.
     * As tuplas encontradas são convertidas no objeto do tipo clazz, onde nome da coluna (redefinida ou não com AS) = nome da propriedade
     */
    public <T> List<T> executeQuery(Class<T> clazz) throws Exception{
        con.setAutoCommit(false);
        List<T> retorno = new ArrayList<T>();
        //verifica se tem cahce para esta consulta
        if(getCache() == null){
            setCache(Collections.synchronizedMap(new HashMap()));
        }
        if(getCache().containsKey(indiceDoCache())){            
            //tem no cache, retorna a lista já montada
            retorno = (List<T>)getCache().get(indiceDoCache());
        }else{            
            //não tem cache executa ela
            ResultSet rs = p.executeQuery();
            while(rs.next()){
                Object objeto = clazz.newInstance();
                ResultSetMetaData meta = rs.getMetaData();
                for(int i = 1; i <= meta.getColumnCount(); i++){
                    String coluna = meta.getColumnLabel(i);
                    //define via reflection a propriedade do objeto que tem o mesmo nome da coluna
                    try{
                        //System.out.println("propriedade: "+coluna+", valor: "+rs.getObject(coluna));
                        BeanUtils.setProperty(objeto, coluna, rs.getObject(coluna));
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }
                retorno.add((T)objeto);
            }
            rs.close();
        }
        p.close();
        return retorno;
    }

   /*Guarda o resultado em cache*/
    public <T> List<T>  executeQueryAndCacheResult(Class<T> clazz) throws Exception{
        con.setAutoCommit(false);
        List<T> retorno = executeQuery(clazz);
        if(getCache() == null){
            setCache(Collections.synchronizedMap(new HashMap()));
        }
        //coloca no cache o resultado, se ainda não adicionado
        if(!cache.containsKey(indiceDoCache())){
            //ainda não está no cache, adiciona
            getCache().put(indiceDoCache(), retorno);
        }
        return retorno;
    }
    
    public PreparedStatementPlus prepareStatement(String sql)throws Exception{
        this.sqlquery = sql;
        parametrosString = new StringBuilder();
        p = con.prepareStatement(sqlquery);
        return this;
    }

    public int executeUpdate() throws Exception{
        int retorno =  p.executeUpdate();
        //sem retorno, pode fechar o preparedStatement
        p.close();
        return retorno;
    }


    //***SETAGEM DOS PARÃMETROS****//
    public PreparedStatementPlus setString(int parametro, String valor) throws Exception{
        p.setString(parametro, valor);
        parametrosString.append(" ?").append(parametro).append(":").append(valor).append("? ");
        return this;
    }

    public PreparedStatementPlus setInt(int parametro, int valor) throws Exception{
        p.setInt(parametro, valor);
        parametrosString.append(" ?").append(parametro).append(":").append(valor).append("? ");
        return this;
    }

    public PreparedStatementPlus setLong(int parametro, long valor) throws Exception{
        p.setLong(parametro, valor);
        parametrosString.append(" ?").append(parametro).append(":").append(valor).append("? ");
        return this;
    }

    public PreparedStatementPlus setDate(int parametro, java.util.Date valor) throws Exception{
        p.setDate(parametro, new java.sql.Date(valor.getTime()));
        parametrosString.append(" ?").append(parametro).append(":").append(valor).append("? ");
        return this;
    }

    /*
     * Formato dd/MM/yyyy
     */
    public PreparedStatementPlus setDate(int parametro, String valor) throws Exception{
        java.util.Date data = new SimpleDateFormat("dd/MM/yyyy").parse(valor);
        p.setDate(parametro, new java.sql.Date(data.getTime()));
        parametrosString.append(" ?").append(parametro).append(":").append(valor).append("? ");
        return this;
    }

    public PreparedStatementPlus setDate(int parametro, java.sql.Date valor) throws Exception{
        p.setDate(parametro, valor);
        parametrosString.append(" ?").append(parametro).append(":").append(valor).append("? ");
        return this;
    }

    public PreparedStatementPlus setTimestamp(int parametro, java.sql.Timestamp valor) throws Exception{
        p.setTimestamp(parametro, valor);
        parametrosString.append(" ?").append(parametro).append(":").append(valor).append("? ");
        return this;
    }

    public PreparedStatementPlus setBoolean(int parametro, boolean valor) throws Exception{
        p.setBoolean(parametro, valor);
        parametrosString.append(" ?").append(parametro).append(":").append(valor).append("? ");
        return this;
    }
    //***FIM DA SETAGEM DOS PARAMETROS***//


    /*
     * O indice é o sqlqueri + parametros
     */
    private String indiceDoCache()throws Exception{
        return sqlquery+parametrosString.toString();
    }

    /**
     * @return the cache. Recupera cache atualizado
     */
    public Map getCache() {
        return cache;
    }

    /**
     * @param cache the cache to set. Seta um cache de fora, segundo nivel armazenado em algum lugar
     */
    public void setCache(Map cache) {
        this.cache = cache;
    }

    public PreparedStatementPlus reset(){
        sqlquery = null;
        parametrosString = new StringBuilder();
        try{p.close();}catch(Exception e){}
        return this;
    }

}


Certamente é preferível o uso de frameworks já consolidados, hibernate, padrão JPA etc. Não é o foco competir com esses frameworks já consagrados. No entanto em casos onde não se pode implantar esses frameworks podemos otimizar os recursos nativos do JDBC para melhorar a produtividade e performance.

Atenciosamente,
Gustavo Marques



Veja também

Related Posts Plugin for WordPress, Blogger...