JavaServer Faces – JSF Fernando Freitas Costa Bacharel em Sistemas de Informação Especialista em Gestão e Docência Universitária blog.fimes.edu.br/fernando [email protected] JavaServer Faces e Banco de Dados Connection Para trabalhar com Banco de Dados (MySQL) em JSF, é necessário primeiramente ter um objeto connection, que pode ser obtido com a sintaxe a seguir: Connection conn = DriverManager.getConnection(“jdbc:mysql://url:porta/nomeBanco ”, “usuario”, “senha”); Statement Depois de obter o objeto connection, é necessário criar um objeto da interface java.sql.Statement, que permitirá o envio dos comandos SQL ao banco de dados. Statement stmt = conn.createStatement(); A interface java.sql.Statement possui entre outros, os métodos: ◦ executeUpdate – envia comandos SQL que atualizam o banco de dados. Retorna um inteiro com o número de registros afetados. Ex: int x = stmt.executeUpdate(“insert into usuario (id, nome, senha) values(1, ‘paulo’, ‘123’)”); ◦ executeQuery – envia comandos SQL que consultam o banco de dados. Retorna um ResultSet. Ex: ResultSet rs = stmt.executeQuery(“select * from usuario”); ResultSet Um ResultSet é como uma tabela de dados que representa um conjunto de registros obtidos de um banco de dados, geralmente pela execução de consultas em um objeto statement. A interface java.sql.ResultSet possui alguns métodos de navegação entre os registros obtidos: ◦ first() - posiciona o cursor no primeiro registro do objeto ResultSet. ◦ last() - posiciona o cursor no último registro do objeto ResultSet. ◦ next() -posiciona o cursor no próximo registro do objeto ResultSet. Retorna false se não houver próximo. ◦ previous() - posiciona o cursor no registro anterior do objeto ResultSet. ◦ etc... ResultSet Para obter os dados de um registro, devemos chamar um dos seguintes métodos da interface java.sql.ResultSet: ◦ getString() – Obtém informações do tipo String; ◦ getInt() – Obtém informações do tipo Inteiro; ◦ getDouble() – Obtém informações do tipo double ◦ E assim sucessivamente de acordo com o tipo de dado a ser obtido. Exemplo: while(rs.next()){ Integer id = rs.getInt(“id”); String nome = rs.getString(“nome”); String senha = rs.getString(“senha”); } Fechando Conexões Após terminar o acesso ao banco de dados, é sempre bom fechar a conexão. Para garantir que ela seja fechada em qualquer situação, mesmo em caso de exceções, coloque o método close() dentro de um bloco try/catch/finally. Uma boa prática neste ponto, é criar blocos try separados para tratar a conexão e para tratar as demais exceções. Ex: try{ Connection conn = DriverManager.getConnection(“jdbc:mysql://url:porta/nomeBanco”, “usuario”, “senha”); try{ Statement stmt = conn.createStatement(); int x = stmt.executeUpdate(“insert into usuario (id, nome, senha) values(1, ‘paulo’, ‘123’)”); }finally{ conn.close(); } }catch(SQLException e){ ... } Outra opção melhor ainda, é definir seu método para lançar SQLException e deixar que o chamador trate estas exceções. PreparedStatement Quando a aplicação precisa emitir várias vezes o mesmo comando SQL apenas com valores diferentes, uma opção mais rápida para trabalhar com o banco de dados é fazer uso da interface java.sql.PreparedStatement. Um objeto PreparedStatement solicita ao banco que uma query seja précompilada. Feito isto, ela é mantida e reutilizada sempre que ela for submetida novamente. Outra boa razão para trabalhar com esta interface é evitar que entradas de usuário se transformem em SQL Injection (ataque a uma aplicação via código SQL). O primeiro passo é criar um objeto da interface java.sql.PreparedStatement, que permitirá o envio dos comandos SQL ao banco de dados. PreparedStatement pstmt = conn.preparedStatement(“instrução sql com parâmetros”); PreparedStatement Para aceitar valores diferentes o objeto PreparedStatement trabalha com o uso de parâmetros (representados pelo ponto de interrogação), que são definidos no momento da execução. Ex: PreparedStatement pstmt = conn.preparedStatement(“select * from usuario where nome like ?”); pstmt.setString(1, ‘fernando’); ResultSet rs = pstmt.executeQuery(); Para definir os valores de cada parâmetro a interface java.sql.PreparedStatement fornece os métodos: ◦ setString() – Define parâmetros do tipo String; ◦ setInt() – Define parâmetros do tipo Integer/int. ◦ setDouble() – Define parâmetros do tipo Double/double. ◦ E assim sucessivamente, de acordo com o tipo do parâmetro a ser definido. ◦ Estes métodos recebem dois parâmetros: O primeiro é um número referente a qual ponto de interrogação você está se referindo (1º, 2º, 3º, ...). O segundo é o valor que deve ser atribuído ao parâmetro. PreparedStatement Assim como a interface java.sql.Statement, a interface java.sql.PreparedStatement também possui entre outros, os métodos: ◦ executeUpdate – envia comandos SQL que atualizam o banco de dados. Retorna um inteiro com o número de registros afetados. Ex: PreparedStatement pstmt = conn.preparedStatement(“insert into usuario (id, nome, senha) values (?, ?, ?)”); pstmt.setInt(1, 1); pstmt.setString(2, ‘fernando’); pstmt.setString(3, ’123’); Integer resultado = pstmt.executeUpdate(); ◦ executeQuery – envia comandos SQL que consultam o banco de dados. Retorna um ResultSet. Ex: PreparedStatement pstmt = conn.preparedStatement(“select * from usuario where nome like ?”); pstmt.setString(1, ‘fernando’); ResultSet rs = pstmt.executeQuery(); Transações Ao trabalhar com banco de dados, pode ser necessário utilizar o conceito de transações para confirmar ou reverter uma ou mais comandos SQL. O uso de transações é bom por duas razões: integridade e acesso concorrente. Por padrão, uma conexão com o banco trabalha no modo autocommit, ou seja, confirma cada comando separadamente. Portanto, ao trabalhar com transações, o primeiro passo é desativar este padrão. Ex: conn.setAutoCommit(false); Transações Se durante a transação tudo correr bem, deve-se efetuar um commit ao final do processo. Ex: conn.commit(); Caso contrário, deve-se efetuar um rollback na transação. Ex: conn.rollback (); O exemplo a seguir mostra como lidar com transações: conn.setAutoCommit(false); boolean commited = false; try{ //operações no banco de dados conn.commit(); commited = true; }finally{ if (!commited) conn.rollback(); } Exemplo Prático JSF e Banco de Dados Crie um novo Dinamic Web Project com o nome ExemploBD01; Em seguida, devemos fazer estes 6 passos. ◦ ◦ ◦ ◦ ◦ ◦ Criação do banco de dados; Criação das tabelas; Criação da classe DAO; Criação da classe Bean; Mapeamento da classe Bean; Criação da página JSF. Passos 1 e 2 – Criação do Banco e da tabela create database jsf_bd; use jsf_bd; create table usuario( id integer not null auto_increment, nome varchar(50) not null, email varchar(50), fone varchar(13) not null, primary key (id) ) engine = InnoDB; Passo 3 – Criação da classe Bean Crie o pacote bean. Crie uma classe chamada UsuarioBean. Ela será o meio termo entre o dao e a página JSF. package bean; import javax.faces.bean.ManagedBean; import dao.UsuarioDAO; @ManagedBean public class UsuarioBean { private Integer id; private String nome, fone, email; public public public public public public public public Integer getId() { return id; } void setId(Integer id) { this.id = id; } String getNome() { return nome; } void setNome(String nome) { this.nome = nome; } String getFone() { return fone; } void setFone(String fone) { this.fone = fone; } String getEmail() { return email; } void setEmail(String email) { this.email = email; } public String salvar(){ UsuarioDAO uDao = new UsuarioDAO(); int resultado = uDao.salvar(this); if(resultado > 0){ this.nome = ""; this.fone = ""; this.email = ""; } return null; } } Passo 4 – Criação da classe DAO Crie um pacote dao Crie uma classe chamada UsuarioDAO. Ela será responsável por lidar diretamente com o BD. Vejamos um exemplo simples: package dao; import import import import import import import java.sql.Connection; java.sql.DriverManager; java.sql.SQLException; java.sql.Statement; javax.faces.application.FacesMessage; javax.faces.context.FacesContext; bean.UsuarioBean; public class UsuarioDAO { public int salvar(UsuarioBean ub) { int resultado = 0; FacesContext ctx = FacesContext.getCurrentInstance(); try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jsf_bd", "root", "vertrigo"); try { Statement stmt = conn.createStatement(); resultado = stmt.executeUpdate("insert into usuario (nome, email, fone) values ('"+ ub.getNome()+ "', '"+ ub.getEmail()+ "', '" + ub.getFone() + "')"); ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Cadastro realizado com sucesso", "O cadastro foi realizado com sucesso")); } finally { conn.close(); } } catch (ClassNotFoundException cnfex) { ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erro: Classe não encontrada", "A classe com.mysql.jdbc.Driver não foi encontrada. Detalhe: "+ cnfex.getMessage())); } catch (SQLException sqlex) { ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Ocorreu um erro ao salvar" , "Ocorreu um erro ao executar a instrução SQL. Detalhe: "+ sqlex.getMessage())); } return resultado; } } Passo 5 – Criação das páginas JSF Crie uma nova página chamada index.xhtml <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> <title>Insert title here</title> </head> <body> <h:form> <h:messages errorStyle="color:red" infoStyle="color:green" showDetail="true"/> <h:panelGrid columns="3"> <h:outputLabel value="Nome:" for="nome"/> <h:inputText id="nome" value="#{usuarioBean.nome}" required="true" requiredMessage="Informe o nome"/> <h:message for="nome"/> <h:outputLabel value="E-mail:" for="email"/> <h:inputText id="email" value="#{usuarioBean.email}"/> <h:message for="email"/> <h:outputLabel value="Fone:" for="fone"/> <h:inputText id="fone" value="#{usuarioBean.fone}" required="true" requiredMessage="Informe o telefone"/> <h:message for="fone"/> <h:commandButton action="#{usuarioBean.salvar()}" value="Salvar" /> </h:panelGrid> </h:form> </body> </html> Tente executar... Funcionou? Por quê não funcionou? Não funcionou porque o Tomcat precisa que a classe com.mysql.jdbc.Driver seja informada. Para isto, você vai precisar do Conector MySQL que pode ser obtido no endereço: http://dev.mysql.com/downloads/connector/j/ Após baixá-lo, descompacte-o e copie o arquivo mysqlconnector-java-5.x.xx-bin.jar (onde o x refere-se as subversões do conector MySQL), para dentro da pasta WebContent/WEB-INF/lib Pronto, agora sim, execute o arquivo. Exercícios 1. Com base no conteúdo passado, faça uma cópia deste projeto e salve com o nome ExemploBD02. No projeto ExemploBD02, faça as alterações para que ele fique mais seguro, ou seja, utilize PreparedStatement no lugar das Statement utilizada. 2. Em seguida, crie uma página sucesso.xhtml que: • • • Deverá ser exibida em caso de sucesso ao cadastrar. Deve exibir os dados recém cadastrados. (Atenção, estes dados devem ser consultados no banco) Coloque nela um botão Voltar.