Programação para Internet II Aulas 07 e 08 Fernando F. Costa professor.fimes.edu.br/fernando nando@fimes.edu.br
Como tratar de grandes conjuntos de resultados Quando temos uma grande quantidade de registros a ser exibida para um usuário, provavelmente não vamos querer que todos eles sejam carregados e exibidos de uma única vez. Uma alternativa seria filtrar a consulta para reduzir os dados a serem exibidos. Contudo paginar os resultados em pequenas quantidades também pode ser uma boa solução.
A interface RowSet Uma boa alternativa para trabalhar com a paginação de resultados é fazer uso da interface RowSet que foi introduzida na especificação JDBC 2.0 para definir um meio padrão para acessar dados armazenados em cache através de um componente JavaBeans ou através de sistemas distribuídos.
O Bean CachedRowSet Portanto, para tratar consultas que são maiores que uma tela inteira e não tão grande para serem verdadeiras devoradoras de memória, o Bean CachedRowSet se mostra como boa opção. Ele faz parte da interface RowSet contida na implementação JDBC 2.0 fornecida pela Sun.
O Bean CachedRowSet Diferente de ResultSet, CachedRowSet é uma conexão offline que armazena em cache todas as linhas de sua consulta no objeto. Nenhuma conexão ativa é necessária porque todos os dados foram pegos do banco de dados. Para fazer uso deste Bean, basta configurar suas propriedades de acesso ao Banco, sua consulta SQL e pronto. A partir deste ponto é só navegar pelos resultados obtidos.
Vejamos como trabalhar com a paginação de resultados em JSP utilizando o Bean CachedRowSet. Para isto, crie um novo projeto web chamado Conferencia. No MySQL crie uma base de dados chamada conf para ilustrar nosso exemplo.
No MySQL, digite os seguintes comandos para criar o banco e as tabelas. create database conf; create table conference( id integer not null auto_increment, cidade varchar(80), aeroporto char(3), assentos integer, primary key (id) ); create table shuttles( id integer not null auto_increment, aeroporto char(3), hora time, assentos integer, primary key (id) );
Digite os seguintes comandos para preencher as tabelas criadas: INSERT INTO conference (cidade, aeroporto, assentos) VALUES ('Austin', 'AUS', 45); INSERT INTO conference (cidade, aeroporto, assentos) VALUES ('Las Angeles', 'LAX', 37); INSERT INTO conference (cidade, aeroporto, assentos) VALUES ('New York', 'JFK', 0); INSERT INTO conference (cidade, aeroporto, assentos) VALUES ('Houston', 'IAH', 11); INSERT INTO conference (cidade, aeroporto, assentos) VALUES ('Boston', 'BOS', 0); INSERT INTO conference (cidade, aeroporto, assentos) VALUES ('San Francisco', 'SFO', 12); INSERT INTO shuttles (aeroporto, hora, assentos) VALUES ('AUS', '9:40', 10); INSERT INTO shuttles (aeroporto, hora, assentos) VALUES ('AUS', '12:00', 12); INSERT INTO shuttles (aeroporto, hora, assentos) VALUES ('AUS', '14:15', 0); INSERT INTO shuttles (aeroporto, hora, assentos) VALUES ('LAX', '12:15', 10); INSERT INTO shuttles (aeroporto, hora, assentos) VALUES ('LAX', '16:35', 10); INSERT INTO shuttles (aeroporto, hora, assentos) VALUES ('IAH', '9:30', 0); INSERT INTO shuttles (aeroporto, hora, assentos) VALUES ('JFK', '12:15', 10); INSERT INTO shuttles (aeroporto, hora, assentos) VALUES ('BOS', '16:35', 10); INSERT INTO shuttles (aeroporto, hora, assentos) VALUES ('SFO', '9:30', 10);
Concluído a parte de criação e preenchimento do banco, é hora de criarmos nosso arquivo JSP que fará a paginação dos resultados obtidos. No exemplo a seguir, os resultados serão paginados de 5 em 5 registros. Você pode definir a quantidade que desejar, basta definir no loop quantos registros serão exibidos por vez.
<%@page import="java.sql.*" %> CachedResults.jsp (parte 1/3) <%@page import="javax.sql.*" %> <%@page import="com.sun.rowset.*" %> <jsp:usebean id="crs" class="com.sun.rowset.cachedrowsetimpl" scope="session"> <% try { Class.forName("com.mysql.jdbc.Driver"); catch (ClassNotFoundException e) { System.err.println("Error" + e); %> <jsp:setproperty name="crs" property="url" value="jdbc:mysql://localhost:3306/conf" /> <jsp:setproperty name="crs" property="username" value="root" /> <jsp:setproperty name="crs" property="password" value="adminadmin" /> <jsp:setproperty name="crs" property="command" value="select * from shuttles order by id" /> <% try { crs.execute(); catch (SQLException e) { out.println("sql Error: " + e); %> </jsp:usebean>
<html> <body> CachedResults.jsp (parte 2/3) <center> <h2>resultados da Consulta em Cache</h2> <P> <table border="2"> <tr bgcolor="tan"> <th>id</th><th>aeroporto</th><th>hora de Partida </th><th>assentos</th></tr> <% try { if ("first".equals(request.getparameter("action"))) crs.beforefirst(); for (int i=0; (i < 5) && crs.next(); i++) { %> <tr> <td><%= crs.getstring("id") %></td> <td><%= crs.getstring("aeroporto") %></td> <td><%= crs.getstring("hora") %></td> <td><%= crs.getstring("assentos") %></td> </tr> <% %> </table>
CachedResults.jsp (parte 3/3) <br> <% if (crs.isafterlast()) { crs.beforefirst(); %> Fim dos registros<br><br> <% catch (SQLException e) { out.println("sql Error" + e); %> <a href="<%= HttpUtils.getRequestURL(request) %>?action=first"> [Início]</a> <a href="<%= HttpUtils.getRequestURL(request) %>?action=next"> [Próximo]</a> </center> </body> </html>
Resultado Esperado
Valor: 2,0 pontos Data apresentação: 11/04/2011 Trabalho em Grupo (3 pessoas) Descrição: Utilizando os conceitos já apresentados, e enfatizando o conceito de Beans e Rowset, crie uma aplicação web para fazer o controle de produtos de uma empresa. A aplicação web deve conter uma parte administrativa na qual deverá ser possível: Cadastrar, Alterar e Remover um produto Na parte de usuário, deve existir apenas uma lista dos produtos cadastrados com as respectivas informações: Nome (String) Preço (Double) Quantidade (Integer) Quando o usuário clicar em comprar um produto, ele deve informar a quantidade. Após confirmar a quantidade, a aplicação deve remover esta quantidade do estoque. Observações: A área administrativa só pode ser acessada mediante login. Não deve ser permitido a compra de uma quantidade superior a quantidade em estoque. Se um produto estiver esgotado (quantidade = 0) deve aparecer a mensagem esgotado no local do preço e não deve ser mostrado o botão Comprar.
Como manter conexões persistentes A conexão com o BD normalmente é a parte mais lenta de uma aplicação web. Contudo, manter várias conexões em aberto pode ser inviável, uma vez que o banco de dados pode suportar uma quantidade limitada de conexões. Desta forma, uma boa alternativa para solucionar este impasse é fazer uso dos Pools de Conexões que são implementados pelo driver do banco de dados, ou por classes de Pool de Conexões.
Pool de Conexões Os pools de conexões mantêm um número fixo de conexões ativas e as empresta quando solicitado pelas suas páginas JSP ou Beans. Logo se tornam uma boa alternativa entre ter muitas conexões abertas e pagar o preço por conexões e desconexões frequentes.
Pool de Conexões A seguir veremos o código que cria um Bean responsável por encapsular uma conexão de banco de dados. Isto permite isolar nossas páginas JSP dos detalhes de conexão com o BD, assim como permite manter nossa conexão através de diversas páginas ao armazená-la na sessão. Desta maneira não precisamos sempre nos reconectar no banco de dados.
ConnectionBean public class ConnectionBean implements HttpSessionBindingListener { private Connection connection; private Statement statement; private static final String driver="com.mysql.jdbc.driver"; private static final String dburl="jdbc:mysql://localhost:3306/conf"; private static final String login="root"; private static final String password="adminadmin"; public ConnectionBean() { try { Class.forName(driver); connection=drivermanager.getconnection(dburl,login,password); statement=connection.createstatement(); catch (ClassNotFoundException e) { System.err.println("ConnectionBean: driver unavailable"); connection = null; catch (SQLException e) { System.err.println("ConnectionBean: driver not loaded"); connection = null;
ConnectionBean public void valuebound(httpsessionbindingevent event) { System.err.println("ConnectionBean: in the valuebound method"); try { if (connection == null connection.isclosed()) { connection = DriverManager.getConnection(dbURL,login,password); statement = connection.createstatement(); catch (SQLException e) { connection = null; public void valueunbound(httpsessionbindingevent event) { try { connection.close(); catch (SQLException e) { finally { connection = null; protected void finalize() { try { connection.close(); catch (SQLException e) {
Processamento de Transações As operações efetuadas em um BD nem sempre são atômicas, ou seja, na maioria das vezes ela envolve duas ou mais operações que precisam acontecer com sucesso para garantir a integridade do banco de dados. Ex: transação bancária.
Processamento de Transações Os Bancos de Dados fornecem um mecanismo conhecido como transações para tratar deste assunto. Em JSP devemos efetuar um commit() no objeto Connection para concluir uma operação que ocorreu sem erros, ou efetuar um rollback() do objeto Connection para retornar o banco ao estado em que ele se encontrava no inicio da transação.
Processamento de Transações Como padrão o JDBC assume que cada instrução SQL é uma transação, e portanto, efetua um autocommit a cada instrução que é emitida. Para que esse controle seja feito manualmente, devemos desativar o recurso autocommit. A seguir temos um exemplo desta situação em uma operação de transação bancária entre as contas de Bob e Sue.
Desativando o recurso autocommit Observação: Lembrem-se de reativar o recurso autocommit ao concluírem suas transações. connection.setautocommit(false); try { Statement st = connection.createstatement(); st.executeupdate( "UPDATE ACCTS SET BALANCE=(BALANCE-100) WHERE OWNER = "Bob"); st.executeupdate( "UPDATE ACCTS SET BALANCE=(BALANCE + 100) WHERE OWNER = "Sue"); connection.commit(); catch (SQLException e) { connection.rollback(); finally { connection.setautocommit(true);
A seguir criaremos um exemplo para trabalhar com os assuntos tratados na aula de hoje. O objetivo do exemplo que será criado é permitir que sejam feitas reservas de vagas em conferências de computação que ocorrerão em todo o mundo. Contudo, o sistema deve garantir que além de reservar uma vaga na conferência, também seja possível reservar uma vaga na ponte aérea com destino a conferência selecionada.
Neste exemplo utilizaremos o projeto Conferencia criado anteriormente, juntamente com a base de dados conf que foi criada.
import java.sql.*; import javax.servlet.http.*; ConnectionBean.java (parte 1 de 3) public class ConnectionBean implements HttpSessionBindingListener { private Connection connection; private Statement statement; private static final String driver="com.mysql.jdbc.driver"; private static final String dburl="jdbc:mysql://localhost:3307/conf"; private static final String login="root"; private static final String password="adminadmin"; public ConnectionBean() { try { Class.forName(driver); connection=drivermanager.getconnection(dburl,login,password); statement=connection.createstatement(); catch (ClassNotFoundException e) { System.err.println("ConnectionBean: driver unavailable"); connection = null; catch (SQLException e) { System.err.println("ConnectionBean: driver not loaded"); connection = null;
ConnectionBean.java (parte 2 de 3) public Connection getconnection() { return connection; public void commit() throws SQLException { connection.commit(); public void rollback() throws SQLException { connection.rollback(); public void setautocommit(boolean autocommit) throws SQLException { connection.setautocommit(autocommit ); public void valuebound(httpsessionbindingevent event) { System.err.println("ConnectionBean: in the valuebound method"); try { if (connection == null connection.isclosed()) { connection = DriverManager.getConnection(dbURL,login,password); statement = connection.createstatement(); catch (SQLException e) { connection = null;
ConnectionBean.java (parte 3 de 3) public ResultSet executequery(string sql) throws SQLException { return statement.executequery(sql); public int executeupdate(string sql) throws SQLException { return statement.executeupdate(sql); public void valueunbound(httpsessionbindingevent event) { try { connection.close(); catch (SQLException e) { finally { connection = null; protected void finalize() { try { connection.close(); catch (SQLException e) {
index.jsp <%@page contenttype="text/html" pageencoding="utf-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/tr/html4/loose.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>jsp Page</title> </head> <body> <center> <h1>conferência Mundial de Computação</h1> <a href="cachedresults.jsp" target="_self">vagas Disponíveis</a><br><br> <a href="conference.jsp" target="_self">fazer minha inscrição</a> </center> </body> </html>
conference.jsp (parte 1 de 2) <%@page import="java.sql.*" errorpage="error.jsp" %> <jsp:usebean id="connection" class="com.taglib.wdjsp.databases.connectionbean" scope="session"/> <html> <body> <center> <font size="+2" face="arial"><b>conferência - Registros</b></font> <form action="shuttle.jsp" method="post"> <table border=1 bgcolor="tan" width="50%" align="center"> <tr> <td> <table border="0" bgcolor="white" cellspacing=0 width="100%"> <tr bgcolor="tan"> <th> </th><th>cidade</th><th>vagas Restantes</th></tr> <tr><% String sql = "select * from conference"; ResultSet results = connection.executequery(sql); while (results.next()) { if (results.getint("assentos") > 0) { %> <td> <input type="radio" name="show" value="<%= results.getstring("id") %>"> </td> <%
conference.jsp (parte 2 de 2) else { %> <td> </td> <% %> <td><%= results.getstring("cidade") %></td> <td align="center"><%= results.getstring("assentos") %></td> </tr> <% %> </table> </td> </tr> </table> <p> <input type="submit" value="proximo (Escolher Ponte Aerea)"> </form> </center> </body> </html>
shuttle.jsp (parte 1 de 2) <%@ page import="java.sql.*" errorpage="error.jsp" %> <jsp:usebean id="connection" class="com.taglib.wdjsp.databases.connectionbean" scope="session"/> <% String showid = request.getparameter("show"); connection.setautocommit(false); String sql; sql = "UPDATE conference set assentos=assentos-1 where id=" + showid; connection.executeupdate(sql); %> <html> <body> <center> <font size="+2" face="arial"><b>reservar Ponte Aérea</b></font> <form action="confirm.jsp" method="post"> <table border=1 bgcolor="tan" width="50%" align="center"> <tr><td> <table border="0" bgcolor="white" cellspacing=0 width="100%"> <tr bgcolor="tan"><th> </th> <th>aeroporto</th><th>hora</th><th>lugares Vagos</th></tr>
<tr> shuttle.jsp (parte 2 de 2) <% sql = "SELECT s.* from shuttles s, conference c where c.id=" + showid + " and s.aeroporto = c.aeroporto"; ResultSet results = connection.executequery(sql); while (results.next()) { if (results.getint("assentos") > 0) { %> <td> <input type="radio" name="shuttle" value="<%= results.getstring("id") %>"> </td> <% else { %> <td> </td> <% %> <td><%= results.getstring("aeroporto") %></td> <td><%= results.gettime("hora") %></td> <td align="center"><%= results.getstring("assentos") %></td> </tr> <% %> </table> </td></tr></table> <input type="hidden" name="show" value="<%= showid %>"> <input type="submit" value="proximo (Revisar Reservas)"> </form> </center></body></html>
confirm.jsp (parte 1 de 2) <%@ page import="java.sql.*" errorpage="error.jsp" %> <jsp:usebean id="connection" class="com.taglib.wdjsp.databases.connectionbean" scope="session"/> <% String sql; String shuttleid = request.getparameter("shuttle"); String showid = request.getparameter("show"); sql = "UPDATE shuttles set assentos=assentos-1 where id=" + shuttleid; connection.executeupdate(sql); sql = "SELECT c.cidade, c.aeroporto, s.hora from conference c, " + "shuttles s where c.id=" + showid + " and s.id=" + shuttleid; ResultSet results = connection.executequery(sql); results.next(); %> <html><body><center> <font size="+2" face="arial"><b>confirmação de Reservas</b></font> <form action="finish.jsp" method=post> <table border=1 bgcolor="tan" width="50%" align="center"> <tr><td>
confirm.jsp (parte 2 de 2) <table border="0" bgcolor="white" cellspacing=0 width="100%"> <tr bgcolor="tan"><th>resumo</th></tr> <tr><td> Reserva requisitada para a conferência na cidade de <b><%= results.getstring("cidade") %></b>, com ponte aérea partindo do aeroporto de <b><%= results.getstring("aeroporto") %></b> às <b><%= results.gettime("hora") %></b>. <p> Para confirmar suas reservas, selecione Confirmar Reservas. </td></tr> </table> </td></tr></table> <p> <input type="submit" name="commit" value="confirmar Reservas"> <input type="submit" name="rollback" value="cancelar Reservas"> </body> </html>
finish.jsp <%@ page import="java.sql.*,com.taglib.wdjsp.databases.*" errorpage="error.jsp" %> <html><body> <% ConnectionBean connection = (ConnectionBean)session.getAttribute("connection"); if (request.getparameter("commit")!= null) connection.commit(); else connection.rollback(); session.removeattribute("connection"); %> <center> <% if (request.getparameter("commit")!= null) { %> <font size="+2" face="arial"><b>confirmação de Reservas</b></font> <p> Sua reserva foi confirmada, obrigado... <% else { %> <font size="+2" face="arial"><b>cancelamento de Reservas</b></font> <p> Suas reservas foram canceladas. <% %> <a href="conference.jsp">outra Reserva</a> </body></html>
error.jsp <%@ page import="java.sql.*,com.taglib.wdjsp.databases.*" iserrorpage="true" %> <html> <body> <% if (exception instanceof SQLException) { try { ConnectionBean connection = (ConnectionBean)session.getAttribute("connection"); connection.getconnection().rollback(); session.removeattribute("connection"); catch (SQLException e) { %> <center> <font size="+2" face="arial"><b>erro na Aplicaçã</b></font> <p> Um erro ocorreu: <tt><%= exception %></tt> <p> <a href="conference.jsp">outra Reserva</a> </center></body></html>
Boa Noite!