Como criar um componente com JSF e PostgreSQL

Veja nesse artigo como criar um componente utilizando o JSF (JavaServer Faces), um framework Java que segue o padrão MVC (Model – View – Controller), utilizando banco de dados PostgreSQL.

Com a crescente demanda no uso de aplicativos em quaisquer dispositivos que contenham recursos necessários para o uso de internet e o surgimento de novas tecnologias, vem aumentando repentinamente e a busca por novas soluções, permitindo uma proposta inovadora para a criação própria de ferramentas que facilitam e agilizam as aplicações web.

Apresentando um conceito inicial, Java é uma linguagem de programação que possui inúmeros recursos por ser multiplataforma: independente de arquitetura e sistema operacional e também possui características como portabilidade, orientação a objetos, além de ser concisa e simples.

Ao ser compilada, a linguagem Java gera um bytecode que consiste num resultado de comunicação, em tempo de execução, entre o sistema operacional e o lado cliente a ser executado para o usuário.

Para criar componentes utilizando JSF, é necessário um ambiente de desenvolvimento capaz de interpretar a linguagem Java, além de fazer a comunicação com o HTML, com o servidor web, com o JDBC (conector para a fonte de dados) e por fim com o banco de dados, tudo isso no formato para a web. Existem inúmeros ambientes de desenvolvimentos, mas recomendamos o NetBeans IDE que se encontra na versão 8.0.1.

Já na parte de banco de dados, há o PostgreSQL que é um banco de dados robusto e possui características simples e sua manipulação é de maneira muito facilitada. Poderíamos escolher qualquer outro banco de dados para ilustrar este artigo, mas o PostgreSQL é muito rápido quando trata das transações de dados. Além disso, o NetBeans IDE já tem em suas opções uma para anexar a biblioteca JDBC própria e pronta para o projeto a ser desenvolvido.

Primeiros Passos para a Criação do Projeto Web

Feito o download do ambiente de desenvolvimento e do gerenciador do banco de dados e ter instalado ambos, agora podemos iniciar a criação de um componente JSF. Após ter aberto o NetBeans IDE, é necessário ir na barra de menus → Arquivo → Novo Projeto. Vai aparecer uma janela de opções, como a demonstrada na Figura 1.

Figura 1. Janela de opções para escolha de um Novo Projeto

Após isso, clicar em Próximo e logo aparecerá uma janela para colocar o nome do projeto (que pode ser Componentes) e o local do mesmo (que pode ser num local seguro e de qualquer preferência) como ilustra a Figura 2.

Figura 2. Configuração do Nome do Projeto e a Localização do Projeto

Feito isso, e ter clicado em Próximo, vai aparecer uma janela para definir qual o servidor web que rodará a aplicação. Por preferência, a escolha é do Apache Tomcat (embora o GlassFish Server também seja muito bom) e a versão do Java EE web é a 7. O caminho do contexto deixa do jeito que foi criado automaticamente, como mostra a Figura 3.

Figura 3. Escolha do Servidor, Versão do Java EE e do Caminho do Contexto

E finalmente, após ter clicado em próximo, aparecerá a última janela de escolha de configuração: por se tratar de JSF, seleciona-se a opção JavaServer Faces e por fim clique em Finalizar, como mostra a Figura 4.

Figura 4. Escolha de Frameworks e Bibliotecas

Projeto criado então agora o próximo passo é colocar em prática a idéia de como será criado o componente e onde será utilizado.

Como a linguagem Java possui recursos de JDBC suficientemente para conectar com o banco de dados, como já havia dito, o PostgreSQL será necessário para a armazenagem de dados onde a aplicação será encarregada de ter uma parte responsável de obter tais dados e mostrar para o usuário.

Adicionando Driver JDBC ao Projeto

Para adicionar o driver JDBC para o projeto, é só clicar com o botão direito em Bibliotecas, navegar até a opção Bibliotecas Disponíveis, selecionar Driver JDBC do PostgreSQL e finalmente clicar em Adicionar Biblioteca, como mostra a Figura 5.

Figura 5. Escolha do Driver JDBC

Inicialmente, ao dar os primeiros passos com a implementação, é necessário criar uma tabela no PostgreSQL com o nome de estados,cujo princípio é ter um conjunto de dados já armazenados que possam facilitar na hora de chamar os mesmos durante a execução do projeto.

Configurando PostgreSQL

Com o PostgreSQL já instalado, agora temos que criar um banco de dados internamente,e para isso é preciso iniciar o gerenciador, que no caso é pgAdmin. Dê dois cliques em cima de PostgreSQL X.Y (onde X.Y é a versão atual instalada, provavelmente são dois números), clique com o botão direito em: Databases e depois em New Database, ou vá à barra de menu superior e selecione a opção Edit e clicar em Create. Em seguida aparecerá a seguinte tela da Figura 6.

Figura 6. New Database com o nome do banco de dados

Colocado um nome, é só clicar em OK. Para conectar, basta dar duplo clique em cima do banco de dados criado e para adicionar a tabela que será com o nome de uf, clique no botão SQL (Execute arbitrary SQL queries), como mostra na Figura 7logo abaixo.

Figura 7. Tela de orientação de propriedades, características e informações internas do banco de dados

Agora é hora de criar de fato a tabela uf e para isso, coloque o seguinte código descrito na Listagem 1 e logo após apertar a tecla F5 e a tabela será criada.

CREATE TABLE uf ( uf_codigo integer NOT NULL, uf_nome character varying(20) NOT NULL, uf_sigla character varying(2) NOT NULL, CONSTRAINT uf_pkey PRIMARY KEY (uf_codigo) ) WITH ( OIDS=FALSE ); ALTER TABLE uf OWNER TO postgres; CREATE INDEX uf_uf_codigo_idx ON uf USING btree (uf_codigo);
Listagem 1. Esta listagem refere-se para a criação de uma tabela de estados com nome de: uf

Após ter executado e não ter dado erro, agora temos que cadastrar alguns estados e, para isso, basta colocar esse conjunto de comandos descritos na Listagem 2 e executar que os estados ficarão armazenados.

insert into uf (uf_codigo, uf_nome, uf_sigla) values (1,'Acre','AC'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (2,'Alagoas','AL'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (3,'Amazonas','AM'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (4,'Amapá','AP'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (5,'Bahia','BA'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (6,'Ceará','CE'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (7,'Distrito Federal','DF'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (8,'Espirito Santo','ES'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (9,'Goiás','GO'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (10,'Maranhão','MA'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (11,'Minas Gerais','MG'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (12,'Mato Grosso do Sul','MS'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (13,'Mato Grosso','MT'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (14,'Pará','PA'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (15,'Paraiba','PB'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (16,'Pernambuco','PE'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (17,'Piauí','PI'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (18,'Paraná','PR'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (19,'Rio de Janeiro','RJ'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (20,'Rio Grande do Norte','RN'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (21,'Rondonia','RO'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (22,'Roraima','RR'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (23,'Rio Grande do Sul','RS'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (24,'Santa Catarina','SC'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (25,'Sergipe','SE'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (26,'São Paulo','SP'); insert into uf (uf_codigo, uf_nome, uf_sigla) values (27,'Tocantins','TO');
Listagem 2. Conjunto de comandos que servem para armazenar valores como código, nome do estado e a sigla dentro da tabela uf

Feito isso, é só fechar a janela do SQL (pode ser sem salvar mesmo), que agora temos que dar continuidade a criação do componente. Mas para isso é preciso ter em mente que antes haverá uma caixa de seleções de itens (selectItems) via HTML para buscar todos os estados cadastrados que estão dentro do banco de dados. E isso se transformará em um componente.

Criação de um Novo Projeto Java Auxiliar

Para que tenha uma comunicação com o banco de dados, é recomendado criar uma Nova Aplicação Java que contenha todos esses requisitos. O nome pode ser DAO (que costumeiramente se usa para dizer que é uma camada intermediária entre a aplicação e o acesso a dados). Após isso é preciso criar três pacotes: um para conexão com o banco de dados, o segundo para encapsular dados e o terceiro para executar comandos de SQL e tratar possíveis erros. Por fim, dentro de cada pacote, as seguintes classes deverão estar conforme demonstra aFigura 8.

Figura 8. Hierarquia de arquivos dentro do Novo Aplicativo Java

Agora, é necessário implementar dentro do arquivo Conexao.java, o seguinte código da Listagem 3, pois o mesmo ficará responsável por conectar com o banco de dados.

package conexao; import java.sql.*; import java.util.logging.Level; import java.util.logging.Logger; public final class Conexao { private static final String usuario = "postgres"; //usuário do banco de dados private static final String senha = "postgres123"; //senha do banco de dados private static final String url = "jdbc:postgresql: //127.0.0.1:5432/postgres"; //driver public static Connection open() { //abrir conexão try { Class.forName("org.postgresql.Driver"); return DriverManager.getConnection(url, usuario, senha); } catch (SQLException | ClassNotFoundException ex) { Logger.getLogger(Conexao.class.getName()) .log(Level.SEVERE, null, ex); return null; } } public static void close(ResultSet rs, Statement st, Connection conn) { if (rs != null) { try { rs.close(); } catch (SQLException e) { } } if (st != null) { try { st.close(); } catch (SQLException e) { } } if (conn != null) { try { conn.close(); } catch (SQLException e) { } } } // fechar conexão public static void close(Statement st, Connection conn) { close(null, st, conn); } public static void close(Connection conn) { close(null, null, conn); } }
Listagem 3. Classe que tem funcionalidade para estabelecer conexão com o banco de dados

Dentro do arquivo DAO.java é necessário programar uma interface e para isso utilizaremos o código descrito na Listagem 4.

package dao; import java.util.List; public interface DAO<T> { /** * Recupera um registro de uma tabela, baseado na sua chave primária * * @param chave a chave primária * @return o registro correspondente, ou * <code>null</code> se nada for encontrado */ public T getSingle(Object... chave); /** * Recupera uma lista de registros * * @return a lista de registros, ou * <code>null</code> se nada for encontrado */ public List<T> getList(); /** * Recupera uma lista de registros, o tamanho da lista é limitado pelo * parâmetro * * @param top o número máximo de registros selecionados * @return a lista de registros, ou * <code>null</code> se nada for encontrado */ public List<T> getList(int top); }
Listagem 4. Código que recupera registro baseado em sua chave primária e também em uma lista de registros de acordo com o seu tamanho

Dentro do arquivo DAOException.java teremos o tratamento de erros e utilizaremos o seguinte código da Listagem 5.

package dao; public class DAOException extends Exception { public DAOException(Throwable cause) { super(cause); } public DAOException(String message, Throwable cause) { super(message, cause); } public DAOException(String message) { super(message); } public DAOException() { super(); } }
Listagem 5. Classe que propõe o tratamento de erros com base em suas exceções

Agora vamos precisar de uma entidade para fazer a busca de dados, então é necessário programar dentro do arquivo UfDAO.java, o seguinte código descrito na Listagem 6.

package dao; import daoentidades.UF; import conexao.Conexao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; public class UfDAO implements DAO<UF> { @Override public UF getSingle(Object... chave) { if (chave[0] instanceof Integer) { Connection conn = Conexao.open(); PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement("select uf_codigo, uf_nome, uf_sigla from uf where uf_codigo = ?"); ps.setInt(1, (Integer) chave[0]); rs = ps.executeQuery(); if (rs.next()) { return new UF(rs.getInt("uf_codigo"), rs.getString("uf_nome"), rs.getString("uf_sigla")); } } catch (SQLException ex) { } finally { Conexao.close(rs, ps, conn); } } return null; } @Override public List<UF> getList() { return getList(0); } @Override public List<UF> getList(int top) { if (top < 0) { return null; } List<UF> lista = null; Connection conn = Conexao.open(); Statement ps = null; ResultSet rs = null; try { ps = conn.createStatement(); rs = ps.executeQuery("select " + (top > 0 ? "top " + top : "") + "uf_codigo, uf_nome, uf_sigla from uf"); lista = new ArrayList<>(); while (rs.next()) { lista.add(new UF(rs.getInt("uf_codigo"), rs.getString("uf_nome"), rs.getString("uf_sigla"))); } } catch (SQLException ex) { } finally { Conexao.close(rs, ps, conn); } return lista; } }
Listagem 6. Classe que faz busca de dados tanto pela chave primária quanto por uma Lista

E por fim é preciso programar uma camada de negócio responsável por receber e enviar as informações, ficando assim dentro do arquivo UF.java, descrito na Listagem 7.

package daoentidades; import java.io.Serializable; import java.util.Objects; public class UF implements Serializable { private Integer codigo; private String nome; private String sigla; public UF() { codigo = null; nome = null; sigla = null; } public UF(Integer codigo, String nome, String sigla) { this.codigo = codigo; this.nome = nome; this.sigla = sigla; } public Integer getCodigo() { return codigo; } public void setCodigo(Integer codigo) { this.codigo = codigo; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getSigla() { return sigla; } public void setSigla(String sigla) { this.sigla = sigla; } @Override public int hashCode() { int hash = 3; hash = 47 * hash + Objects.hashCode(this.codigo); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final UF other = (UF) obj; return Objects.equals(this.codigo, other.codigo); } }
Listagem 7. Classe responsável por enviar e obter informações de acordo com o banco de dados e o modo cliente

Adicionando o Projeto Java ao Projeto Java Web

Agora precisamos finalizar essa parte de acesso, manipulação e comunicação entre os dados e em como serão tratados daqui em diante. Para continuar com o projetoComponentes, o mesmo tem que estar aberto no Netbeans IDE e a aplicação Java DAO, que foi criada anteriormente, deve ser adicionada como parte de uma biblioteca dentro da aplicação web. Para isso, tem que fazer o mesmo que foi feito quando adicionou-se o driver JDBC, só que ao invés de adicionar uma biblioteca, tem que escolher a opção de Adicionar um Projeto, conforme mostra na Figura 9 abaixo.

Figura 9. Hierarquia da Aplicação web e do Arquivo Java adicionado

Criação de Classes Auxiliares

Seguindo a Figura 9, surge a proposta de criar um pacote chamado Estados dentro do Pacote de Códigos-Fonte. Para isso, cria-se uma classe com o nome de ListaDeEstados.java cuja funcionalidade é armazenar todos os valores obtidos num Array List, de acordo com o seguinte código descrito na Listagem 8.

package Estados; import dao.UfDAO; import daoentidades.UF; import java.io.Serializable; import java.util.List; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; import javax.faces.component.UISelectOne; import javax.faces.event.AbortProcessingException; import javax.faces.event.AjaxBehaviorEvent; import javax.faces.event.ValueChangeEvent; import javax.faces.event.ValueChangeListener; @ManagedBean @ViewScoped public final class ListaDeEstados implements Serializable, ValueChangeListener { private final List<UF> estadosCadastrados; public ListaDeEstados() { UfDAO dao = new UfDAO(); estadosCadastrados = dao.getList(); } @Override public void processValueChange(ValueChangeEvent vce) throws AbortProcessingException { escolheuEstado(vce); } public void escolheuEstado(ValueChangeEvent evt) { UF estado = (UF) evt.getNewValue(); } public void selecionaEstado(AjaxBehaviorEvent event) { UF estado = (UF)((UISelectOne)event.getComponent()).getValue(); } public List<UF> getEstadosCadastrados() { return estadosCadastrados; } }
Listagem 8. Classe responsável por trazer os estados cadastrados num Array List de modo a ficar na memória

Após isso, só é preciso criar mais uma classe, agora com o nome de UFConverter.java, de acordo com a Listagem 9.

package Estados; import dao.UfDAO; import daoentidades.UF; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import javax.faces.convert.FacesConverter; @FacesConverter("UFConverter") public class UFConverter implements Converter { @Override public Object getAsObject(FacesContext fc, UIComponent uic, String string) { if (string != null) { try { int codigo = Integer.parseInt(string); UF estado = (new UfDAO()).getSingle(codigo); return estado; } catch (NumberFormatException ex) { throw new ConverterException( new FacesMessage("Erro convertendo UF!")); } } return null; } @Override public String getAsString(FacesContext fc, UIComponent uic, Object objeto) { if (objeto == null) { return ""; } final UF uf = (UF) objeto; if (uf.getCodigo() == null) { return null; } return uf.getCodigo().toString(); } }
Listagem 9. Classe que possui como funcionalidade converter o código do estado em String e a String em objeto a ser disponibilizado para ser visível ao componente

Criando Arquivo Componente

Finalizando essa parte de criação de classes, agora é a hora tão esperada que consiste na criação do componente JSF. Para isso, com base na Figura 9, clique com o botão direito em Páginas WebNovoOutros e depois selecione JavaServer Faces. Por fim, selecione Componente Composto do JSF. Clicando em próximo, vai aparecer a seguinte tela da Figura 10.

Figura 10. Tela de criação de um novo componente JSF
Nota: É preciso colocar um Nome do Arquivo e obrigatoriamente o componente deve estar na pasta resources\xxx, onde xxx é um nome de uma subpasta. Feito isso, basta clicar em Finalizar.
Nota: Na Figura 10 está aparecendo uma mensagem de aviso em vermelho: “estado.xhtml already exist”. Isso acontece porque há componente com esse mesmo nome já existente no projeto.

Agora a página de criação de componente está pronta para ser programada. Como a ideia inicial é buscar todos os estados cadastrados, então com o código descrito na Listagem 10 dentro do arquivo componente criado estado.xhtml, já é suficiente para que traga os dados esperados.

<?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:cc="http://xmlns.jcp.org/jsf/composite" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:f="http://xmlns.jcp.org/jsf/core"> <cc:interface> <cc:attribute name="valor" required="true"/> </cc:interface> <cc:implementation> <h:outputLabel for="selEstado"><br/>Estados</h:outputLabel> <h:selectOneMenu id="selEstado" immediate="true" value="#{cc.attrs.valor}" required="true" requiredMessage="Escolha um estado!"> <f:converter converterId="UFConverter"/> <f:selectItem itemLabel="-- Selecione um --" itemValue="#"/> <f:selectItems value="#{listaDeEstados.estadosCadastrados}" var="uf" itemLabel="#{uf.nome}" itemValue="#" /> </h:selectOneMenu> </cc:implementation> </html>
Listagem 10. Arquivo componente estado.xhtml e sua implementação com interface e códigos HTML que servem para buscar os dados dentro de um selectItems

Chamando Componente para Página Principal

Finalmente, após ter feito a implementação da página de componente, agora a proposta é chamar o componente para a página inicial. Ao criar uma aplicação Java web, sempre é vindo automaticamente um arquivo denominado index.xhtml, que é onde tudo se inicia, pois todas as páginas devidamente configuradas e ligadas a ela serão seguidas através dessa página. Ficará da mesma forma que o código da Listagem 11o index.xhtml.

<?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://xmlns.jcp.org/jsf/html" xmlns:ex="http://xmlns.jcp.org/jsf/composite/ex"> <h:head> <title>Facelet Title</title> </h:head> <h:body> <h:form prependId="false"> Hello from Facelets <ex:estado valor=""/> </h:form> </h:body> </html>
Listagem 11. Arquivo index.xhtml onde consta o código HTML e também há a chamada do componente criado: estado

Para concluir com o projeto, é necessário salvar todos os arquivos, mas o recomendado é ir salvando arquivo por arquivo.

Agora é só executar o projeto (pode ser usando a tecla F6) e se rodar no Google Chrome, ficará de acordo com a Figura 11.

Figura 11. Projeto pronto sendo executado dentro do navegador Google Chrome

Lembrando que funciona na maioria dos navegadores existentes, mas o Mozilla Firefox é browser padrão do Netbeans IDE.

Para a criação de componentes JSF é preciso já ter um conhecimento básico/intermediário sobre a linguagem Java e HTML e principalmente na área de orientação a objetos. Qualquer passo ou procedimento não executado ou caso tenha se esquecido de programar qualquer passo descrito, você pode usar o código-fonte disponível pra download nesse post.

Artigos relacionados