terça-feira, 5 de fevereiro de 2013

Aplicação web com JSF+JDBC+PRIMEFACES

No post de hoje, passo-a-passo a implementação de uma simples aplicação web (agenda de contatos) com banco de dados, será utilizado as seguintes tecnologias:
  • Java Serve Faces(JSF) :  framework MVC de aplicações web baseado  em Java.
  • Prime Faces: biblioteca de componentes ricos para JSF.
  • Java Database Connectivity JDBC:  conjunto de classes e interfaces escritas em java, que fazem envio de instruções SQL para qualquer BD relacional.

1. Requisitos.

Embora o netbeans venha com a biblioteca do primefaces, ela não é a mais recente, então é aconselhado o download dela direto do site. 

2. Criando projeto.

1.Arquivo > novo projeto > java web > aplicação web > próximo
2.Escreva o nome da aplicação (no meu caso agenda) > próximo
3.Selecionado o servidor GlassFish e java EE deixe a versão default> próximo
4.Selecione o framework JSF e deixe as configurações default, não precisa selecionar nenhuma biblioteca de componentes, adicionaremos posteriormente, clique em finalizar
3. Estrutura de pacotes do projeto

Crie os pacotes 
  1. DAO
  2. modelo
  3. controle
4. Adicionando os JAR's a biblioteca

Adicione os JAR's:

  • primefaces-versão.jar
  • all-themes-versão.jar

  • Adicione a biblioteca JDBC postgresSQL

...se você chegou até aqui a estrutura do projeto deve estar assim:

5. Incluindo cabeçalho

Abra a pagina index.xhtml gerada automaticamente na criação do projeto, e faça a inclusão do cabeçalho do primefaces.   xmlns:p="http://primefaces.org/ui"

Feito isso você esta apto a fazer uso de qualquer componente do primefaces, para o uso dos componentes a sintaxe é semelhante a sintaxe padrão dos componentes do JSF, a diferença é que em vez da letra "h" utilizaremos "p". Ex:
No site do primefaces, existe diversos exemplos em código, para todos os componentes, vale a pena conferir:  http://www.primefaces.org/showcase/ 

6. Setando o tema utilizado


Com o prime existe ainda a possibilidade de utilizar diversos outros temas, para utilizar qualquer tema, basta ter o jar com todos os temas(all-themes-versão.jar) adicionado ao seu projeto, e adicionar o seguinte código no arquivo web.xml dentro e WEB-INF.

 <context-param>   
     <param-name>primefaces.THEME</param-name>   
     <param-value>start</param-value>   
   </context-param>  

No caso start é o tema que estou utilizando. O nome dos temas estão no endereço em comunity:


7. Criando banco e tabela(postgres)

Primeiro crie um novo banco de dados no SGBD, nome "exJSF", dono "postgres" e o resto deixe default.


Selecione o banco criado exJSF e clique em SQL

Cole o código SQL abaixo e aperte F5 para executa-lo

 CREATE TABLE contatos   
 (   
    id serial PRIMARY KEY,   
    nome VARCHAR,   
    telefone VARCHAR  
 )  

Se tudo estiver ok, essa será a estrutura do seu banco:
8. Criando classes


No pacote modelo criaremos uma classe:
  1. ContatosModelo
Essa classe deve ter os mesmo atributos da tabela, todos encapsulados: (private) com os GETERES e SETERES, não precisa implementar o construtor.

package modelo;   
 /**  
  *  
  * @author Filipe Damasceno  
  */  
 public class ContatosModelo {  
   private int id;  
   private String nome;  
   private String telefone;  
     
     
   public int getId() {return id;}  
   public void setId(int id) {this.id = id;}  
   
   public String getNome() {return nome;  }  
   public void setNome(String nome) {this.nome = nome;  }  
   
   public String getTelefone() {return telefone;  }  
   public void setTelefone(String telefone) {this.telefone = telefone;  }  
 }  

No pacote DAO criaremos duas classes java:
  1. Conexao
  2. ContatosDAO
Conexao.java é a classe responsável pela conexão com o banco, ela tem os atributos de conexão, e apenas um método, que retorna uma conexão pronta para uso, o método é estático para que todas as instancias sejam únicas,  e assim não seja possível varias conexões diferentes abertas ao mesmo tempo.


package DAO;  
   
 import java.sql.Connection;  
 import java.sql.DriverManager;  
   
 /**  
  *  
  * @author Filipe Damasceno  
  */  
 public class Conexao {  
   private static final String USER = "postgres";  
   private static final String PASS = "admin";  
   private static final String DRIVER = "org.postgresql.Driver";  
   private static final String URL = "jdbc:postgresql://localhost:5432/exJSF";  
    
   public static Connection getConnectionManual() throws Exception {  
    
     Connection constatic = null;  
    
     Class.forName(DRIVER);  
     DriverManager.setLoginTimeout(1);  
     constatic = DriverManager.getConnection(URL, USER, PASS);  
    
     return constatic;  
   }  
 }  

USER: usuário atribuído ao seu banco no momento da criação;
PASS: senha do usuário atribuído ao banco;
DRIVER: drive de conexão
URL: qual jdbc vamos utilizar, ip e porta, e por ultimo o nome do banco que criamos;

A classe ContatosDAO.java, é a classe responsável pela comunicação com o banco, através da conexão fará as operações de CRUD(Create, Read, Update, Delete) utilizando SQL, ela não tem nenhum atributo, porem possui 3 métodos:

  1.  insert: recebe um objeto ContatosModelo.java como parâmetro, e faz a inserção no banco através de uma conexão retornada de Conexão.java; retorna true se tudo ocorrer bem;
  2. delete: recebe um objeto ContatosModelo.java como parâmetro, e faz a deleção no banco através de uma conexão retornada de Conexão.java; retorna true se tudo ocorrer bem;
  3. selectALL: seleciona todos os dados da tabela contatos através da conexão e retorna um ResultSet onde tem todas as linhas da minha consulta;


 package DAO;  
   
 import java.sql.Connection;  
 import java.sql.PreparedStatement;  
 import java.sql.ResultSet;  
 import modelo.ContatosModelo;  
   
   
 //Filipe Damasceno  
 public class ContatosDAO {  
   
   //METODO DE INSERÇÂO NA TABELA contatos  
   public boolean insert(ContatosModelo contatos){  
     try{   
       Connection con = Conexao.getConnectionManual();  
       PreparedStatement stm = con.prepareStatement("INSERT INTO contatos (nome,telefone) VALUES ('"+contatos.getNome()+"','"+contatos.getTelefone()+"')");  
       stm.execute();   
       con.close();   
       return true;  
     }catch(Exception x){  
       return false;  
     }  
   }  
   public boolean delete(ContatosModelo contatos){  
     try{   
       Connection con = Conexao.getConnectionManual();  
       PreparedStatement stm = con.prepareStatement("DELETE FROM contatos WHERE id="+contatos.getId());  
       stm.execute();   
       con.close();  
       return true;  
     }catch(Exception x){  
       return false;  
     }  
   }  
   public ResultSet selectAll(){  
     try{   
       Connection con = Conexao.getConnectionManual();  
       PreparedStatement stm = con.prepareStatement("SELECT * FROM contatos");  
       ResultSet result = stm.executeQuery();  
         
       con.close();  
       return result;    
     }catch(Exception x){        
       return null;  
     }  
   }   
     
 }  

No pacote controle criaremos uma classe:
  1. ContatosControle
Essa classe irá se comunicar com nossa pagina xHTML (view), essa será a única classe visível a partir das views, e ela é responsável por controlar as interações. Possui dois atributos do tipo ContatosModelo e os dois devem estar encapsulados, é importante ter os Geteres e Seteres, se não o framework JSF não funcionará corretamente.  Os 4 métodos da classe de controle são:

  1.  insert: Valida o dado a ser inserido e se tudo estiver correto faz a inserção
  2. delete: Valida o dado a ser inserido e se tudo estiver correto faz a deleção
  3. selectALL: retorna uma lista com todos os contatos;
  4. cadastroValido: verifica se os dados estão ok;

Depois de criar a classe é necessário colocar duas anotações, uma para informar que essa classe será no ManagedBean (controle), e outra para informar o escopo (request), RequestScoped fará a classe ser instanciada a cada requisição de usuário. 
@ManagedBean
@RequestScoped


 package controle;  
   
 import DAO.ContatosDAO;  
 import java.sql.ResultSet;  
 import java.sql.SQLException;  
 import java.util.ArrayList;  
 import java.util.List;  
 import java.util.logging.Level;  
 import java.util.logging.Logger;  
 import javax.faces.application.FacesMessage;  
 import javax.faces.bean.ManagedBean;  
 import javax.faces.bean.RequestScoped;  
 import javax.faces.context.FacesContext;  
 import modelo.ContatosModelo;  
 import org.primefaces.context.RequestContext;  
   
 /**  
  *  
  * @author Filipe Damasceno  
  */  
 @ManagedBean  
 @RequestScoped  
 public class ContatosControle {  
   private ContatosModelo contato;  
   private ContatosModelo contatoSelecionado;  
     
   public ContatosControle(){  
     contato = new ContatosModelo();  
     contatoSelecionado = new ContatosModelo();  
   }  
   public boolean cadastroValido(){  
     if(!contato.getNome().equals("")){  
       if(!contato.getTelefone().equals("")){  
         return true;  
       }else{  
         FacesContext.getCurrentInstance().addMessage("msg", new FacesMessage(FacesMessage.SEVERITY_INFO, "Erro!","Digite o telefone"));  
         RequestContext.getCurrentInstance().execute("camposObrigatorios()");  
         return false;  
       }  
     }else{  
       FacesContext.getCurrentInstance().addMessage("msg", new FacesMessage(FacesMessage.SEVERITY_INFO, "Erro!","Digite o nome"));  
       RequestContext.getCurrentInstance().execute("camposObrigatorios()");  
       return false;  
     }  
   }   
     
   public void insert(){  
     if(cadastroValido()){  
       new ContatosDAO().insert(contato);        
       RequestContext.getCurrentInstance().execute("PF('cadastro').hide()");      
     }   
   }  
   public void delete(){      
     if(new ContatosDAO().delete(contatoSelecionado)){  
       FacesContext.getCurrentInstance().addMessage("msg", new FacesMessage(FacesMessage.SEVERITY_INFO, "Contato exluido",""));  
     }else  
       FacesContext.getCurrentInstance().addMessage("msg", new FacesMessage(FacesMessage.SEVERITY_INFO, "Selecione algum contato",""));  
     RequestContext.getCurrentInstance().execute("PF('deleta').hide()");      
   }  
   public List<ContatosModelo> selectAll(){  
     ResultSet rs = new ContatosDAO().selectAll();  
     List<ContatosModelo> list = new ArrayList<ContatosModelo>();   
     ContatosModelo contatos;      
     try {  
       while(rs.next()){  
         contatos = new ContatosModelo();  
         contatos.setId(Integer.parseInt(rs.getString("id")));   
         contatos.setNome(rs.getString("nome"));   
         contatos.setTelefone(rs.getString("telefone"));    
         list.add(contatos);     
       }  
         return list;  
     } catch (SQLException ex) {  
         
     }      
     return null;  
   }  
   
   public ContatosModelo getContato() {  
     return contato;  
   }  
   public void setContato(ContatosModelo contato) {  
     this.contato = contato;  
   }  
   public ContatosModelo getContatoSelecionado() {  
     return contatoSelecionado;  
   }  
   public void setContatoSelecionado(ContatosModelo contatoSelecionado) {  
     this.contatoSelecionado = contatoSelecionado;  
   }  
   
 }  

Considerações: essa sem duvida é uma das classes mais importantes, e vou ressaltar alguns pontos que acho importante:
  1. O método selectALL tem um atributo ResultSet que recebe a consulta feita no banco através da nossa classe ContatosDAO, depois é criada uma lista de ContatosModelo, onde cada linha da tabela é setada em uma instancia de ContatosModelo e adicionada a lista, assim a lista tem um objeto ContatosModelo para cada linha.
  2. FacesContext, esta sendo utilizado para passar mensagens para a view, ele recupera a instancia da pagina, e assim manda mensagem para pagina especifica que executou o método, o primeiro atributo passado é o id do componente que recebera e exibirá a mensagem na tela.
  3. RequestContext, está sendo utilizado para chamar os métodos javascript da view, e fechando diálogos da view

9. Criando a VIEW.
Nossa tela inicial ficará assim:
Ela possui um datatable, três commandButton um fieldset e três dialog:
Código da pagina index


<?xml version='1.0' encoding='UTF-8' ?>  
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
<html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"  
      xmlns:p="http://primefaces.org/ui">   
    <h:head>  
        <title>Agenda</title>      
    </h:head>  
    <h:body>     
        <!--............................................................................PAINEL CENTRAL-->  
        <br/><br/><br/>        
        <p:fieldset legend="AGENDA" toggleable="true" toggleSpeed="500">          
            <h:form id="form">                
                <center>  
                    <p:dataTable id="tabela" var="item" value="#{contatosControle.selectAll()}" rowKey="#{item.id}" selectionMode="single"    
                                 scrollable="true" scrollHeight="300" scrollWidth="850" selection="#{contatosControle.contatoSelecionado}">  
                        <p:column headerText="Nome" width="100">  
                            <h:outputText value="#{item.nome}"></h:outputText>  
                        </p:column>   
                        <p:column headerText="Telefone" width="100">  
                            <h:outputText value="#{item.telefone}"></h:outputText>  
                        </p:column>  
                    </p:dataTable>  
                </center>   
            </h:form>   
            <center>  
                <p:commandButton value="Cadastrar" icon="ui-icon-document" update=":form2:displayCadastro" oncomplete="PF('cadastro').show();"/>   
                <p:commandButton value="Vizualizar" icon="ui-icon-search" oncomplete="PF('visualiza').show();" update=":displayVisualiza"/>   
                <p:commandButton value="Deletar" icon="ui-icon-disk" oncomplete="PF('deleta').show();" update=":displaydeleta"/>  
            </center>              
        </p:fieldset>   
        <!--............................................................................MENSAGENS DO SISTEMA-->        
        <!--=======================================================================================================================   
                  TODAS AS MENSAGENS EXIBIDAS NA TELA  
                  SERÃO EXIBIDAS UTILIZANDO ESSE COMPONENTE growl,   
                  QUE EH CHAMADO PELO ID E REFERENCIADO DE DENTRO DO BEAN PELO for  
        ===========================================================================================================================-->        
        <p:growl id="menssagem" showDetail="true" life="3000" for="msg"/>   
        <!--............................................................................DIALOGOS E FUNÇÕES-->  
        <!--CADASTRO DE CONTATO-->             
        <p:dialog id="dialogCadastro" header="Cadastrar contato" widgetVar="cadastro" resizable="false"   
                  width="300" showEffect="clip" hideEffect="explode">   
            <h:form id="form2">  
                <h:panelGrid id="displayCadastro" columns="2" cellpadding="4">   
                    <h:outputText value="*Nome:" />    
                    <br/>  
                    <p:inputText value="#{contatosControle.contato.nome}" />   
                    <br/>  
                    <h:outputText value="*Telefone:" />   
                    <br/>  
                    <p:inputMask value="#{contatosControle.contato.telefone}" mask="(99)9999-9999"/>  
                    <br/>                      
                    <p:commandButton value="Cadastrar" icon="ui-icon-search" action="#{contatosControle.insert()}" update=":form:tabela,:menssagem"/>   
                </h:panelGrid>   
            </h:form>  
        </p:dialog>    
        <script type="text/javascript">
            function camposObrigatorios() {
                jQuery('#form2').effect("shake", {times: 3}, 100);
            }
        </script>  
        <!--VISUALIZAÇÃO DE CONTATO-->             
        <p:dialog id="dialogVisualiza" header="Vizualizar contato" widgetVar="visualiza" resizable="false"   
                  width="400" showEffect="clip" hideEffect="fold">   
            <h:panelGrid id="displayVisualiza" columns="2" cellpadding="4">   
                <h:outputText value="Nome:" />   
                <h:outputText value="#{contatosControle.contatoSelecionado.nome}" />   
                <h:outputText value="Telefone:" />   
                <h:outputText value="#{contatosControle.contatoSelecionado.telefone}" />           
            </h:panelGrid>  
        </p:dialog>    
        <!--DELEÇÂO DE CONTATO-->        
        <p:dialog id="dialogDeleta" header="Vizualizar contato" widgetVar="deleta"   
                  width="400" showEffect="clip" hideEffect="fold">   
            <h:panelGrid id="displaydeleta" columns="2" cellpadding="4">   
                <h:outputText value="Deseja exluir realmente o contato?" />  
                <br/>  
                <p:commandButton value="Sim" action="#{contatosControle.delete()}" update=":form:tabela,:menssagem"/>   
                <p:commandButton value="Não" oncomplete="deleta.hide()" update=":form:tabela,:menssagem"/>   
            </h:panelGrid>  
        </p:dialog>  
    </h:body>    
</html>  

Considerações sobre a pagina:

  1. <p:growl id="menssagem" showDetail="true" life="3000" for="msg"/> esse componente é responsável por receber as mensagens da classe de controle, a mensagem permanece na tela por 3 segundos e desaparece
  2. camposObrigatorios() essa função javascript é chamada também a partir do controle quando o usuário não digita os campos corretamente, a função exibe uma animação que balança os componentes do dialogo de cadastro
  3. No cadastro, quando você faz <p:inputText value="#{contatosControle.contato.nome}" /> você esta atribuindo o campo imput ao atributo nome do objeto contato declarado na classe contatos controle, e o JSF automaticamente sabe quando utilizar o SET ou GET.
  4. No data table <p:dataTable id="tabela"  var="item" value="#{contatosControle.selectAll()}" rowKey="#{item.id}" selectionMode="single"   você tem ai 'var' que é a variável para se referir a lista que vai ser retornada do nosso controle pelo metodo selectALL, rowKey é o atributo que vai servir como id, mesmo que não mostre todos os dados na tabela, o var guarda todas as colunas do retorno, lembrando que o retorno é uma lista<ContatosModelo> ou seja uma lista de objetos ContatosModelo, sendo assim para atribuir a uma coluna do datatable  basta pegar a variável e campo "intem.nome".
Conclusão:

O objetivo foi mostrar como fazer uma aplicação web com as funcionalidades básicas de (cadastro, remoção, e visualização).  Esses são os conceitos básicos(iniciante) para o desenvolvimento de aplicações web com JSF.


  

21 comentários:

  1. Muito bom o poste!
    E como veem muito bem detalhado o poste permita eu acrescentar o meu detalhe
    *Não esqueçam de adicionar o .jar do drive do postgresql ou do banco de dados que você estiver usando #)

    ResponderExcluir
    Respostas
    1. Grato!, Mas só para esclarecer o porque não adicionei o jar, é pelo fato de que apenas o import na classe que irá utilizar conexão com banco já resolve:

      import java.sql.DriverManager;
      import java.sql.SQLException;

      Depois basta setar o driver que irá utilizar, no nosso caso:

      Class.forName("org.postgresql.Driver");

      Isso porque JDBC é visto como serviço, na IDE(netbeans), se você clicar na aba serviços vai ver que la já esta o PATH dos bancos mais utilizados, bastando apenas setar.

      Mas se adicionar o jar também vai dar certo.

      Excluir
  2. bom post, direito a figura... parabens

    ResponderExcluir
    Respostas
    1. Brigado pelo comentario! Logo estarei disponibilizando novas postagens sobre o tema JSF.

      Excluir
  3. Boa tarde Filipe, muito bom o post, mas na classe ContatosControle, na primeira Exception do Logger, o que seria "Utilitario" é uma outra classe, pois da erro e o teu import util.utilitario não existe essa biblioteca
    agradeço

    ResponderExcluir
    Respostas
    1. Opa Claudio, grato! Sim Utilitário seria uma classe sim, porem acabei não fazendo mais uso dela e esqueci de tirar o import e Logger, obrigado por comentar logo estarei corrigindo.

      Excluir
  4. Cara, não sei exatamente porque, mas o prime 3.5 não está rodando, testei outra versão a 2.2.1 e rodou lindo, mas daí dá conflito com alguns dos recursos utilizados, tem alguma configuração diferenciada para essa versão mais nova?

    ResponderExcluir
    Respostas
    1. Ola Anderson, para utilizar o prime 3.5 basicamente se deve seguir 3 passos:
      1-Baixar o jar do prime 3.5
      2- Incluir o jar na biblioteca
      3- E por fim fazer a inclusão do cabeçalho
      Se você fez esses três passos e ainda assim não funcionou, realmente não sei o que houve. Caso queira me passe seu e-mail que posso da uma olhada no seu projeto.
      Att.

      Excluir
  5. Olá filipe muito bom post eu fiz esse sisteminha mas quando eu executo ele não roda esta dando esse erro aqui.

    java.lang.NullPointerException

    ResponderExcluir
    Respostas
    1. Olá, essa exceção pode ser disparada em diversos casos. O primeiro deles é caso você esteja tentando acessar um objeto que não foi instanciado (new ...). Tambem pode ser a biblioteca do postgres, não foi colocado isso no post. Basta clicar com direito em biblioteca, selecionar "adicionar biblioteca" ai procura e adiciona "Drive JDBC do postgresql". Após isso executa novamente o projeto e ver se resolve.

      Excluir
  6. javax.el.ELException: /index.xhtml @41,135 value="#{contatosControle.selectAll()}": java.lang.NullPointerException
    esse erro eu copiei do net beans nao funcionou.

    ResponderExcluir
  7. me passa seu email e melhor de tira duvidas.
    vc é do ceara Filipe? eu tambem sou dai mas moro em brasilia

    ResponderExcluir
  8. fILIPE EU FIS DO MESMO JEITO QUE ESTA NO BLOG QUE VC FES E NÃO RODOU

    ResponderExcluir
  9. responde ai filipe o cometario

    ResponderExcluir
    Respostas
    1. Olá é referente a duvida do Juan Medeiros? Diz teu e-mail ai nos comentários que falo contigo.

      Excluir
  10. ok meu email é franciscoprogramadorr@gmail.com é aquela duvida fiz o sisteminha que você ensina mas deu esse erro que você ve ai no cometario.
    Obrigado conterraneo.ta no nome du ruan porque esse email era dele mas pode me responder agora por email ok.

    ResponderExcluir
  11. ola felipe como e onde ficaria a classe view, desculpe a pergunta eh que sou do tempo dom cobol

    ResponderExcluir
  12. Boa tarde Felipe como e onde ficaria a classe view, desculpe a pergunta eh que sou do tempo do cobol
    meu email é assjjc@gmail.com

    ResponderExcluir
    Respostas
    1. Olá, a view no caso seria a pagina .xhtml, e não a classe, ou seja, seria a sua 'tela'. Nesse exemplo seria a pagina "index", que já é criada por padrão na criação do projeto.

      Excluir

Deixe um comentário!