Implementação de Paginação com JDBC e MySQL

Devido a desvantagens de desempenho, como o alto consumo de recursos, ao utilizar o cursor do ResultSet para implementar paginação de dados, uma abordagem mais eficiente em desenvolvimento real é empregar o mecanismo de paginação nativo do banco de dados.

A seguir, apresentaremos um exemplo utilizando o mecanismo de paginação do banco de dados MySQL.

Primeiro, criamos a classe ItemProduto, responsável por encapsular as informações dos produtos. Esta clasce funciona como um JavaBean para representar objetos de produto.

package modelo;

import java.math.BigDecimal;

public class ItemProduto {
    public static final int TAMANHO_PAGINA = 2; // Número de registros por página
    private int idProduto; // Identificador
    private String nomeProduto; // Nome
    private BigDecimal precoProduto; // Preço

    public int getIdProduto() {
        return idProduto;
    }

    public void setIdProduto(int idProduto) {
        this.idProduto = idProduto;
    }

    public String getNomeProduto() {
        return nomeProduto;
    }

    public void setNomeProduto(String nomeProduto) {
        this.nomeProduto = nomeProduto;
    }

    public BigDecimal getPrecoProduto() {
        return precoProduto;
    }

    public void setPrecoProduto(BigDecimal precoProduto) {
        this.precoProduto = precoProduto;
    }
}

Na classe ItemProduto, encapsulamos as informações básicas do objeto produto. Além disso, definimos o tamanho da página para paginação.

A seguir, criamos a classe RepositorioProduto, responsável por operações de banco de dados relacionadas aos produtos. Esta classe representa a camada de acesso a dados (DAO) para informações de produto.

package repositorio;

import modelo.ItemProduto;
import util.ConexaoBD;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class RepositorioProduto {

    /**
     * Busca todos os produtos com paginação
     *
     * @param pagina Número da página
     * @return Lista de produtos
     */
    public List<ItemProduto> buscarTodosProdutos(int pagina) {
        List<ItemProduto> listaProdutos = new ArrayList<>();
        Connection conexao = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        // A cláusula LIMIT especifica o ponto de início e o número de registros
        String sql = "SELECT id_produto, nome_produto, preco_produto FROM produto ORDER BY id_produto LIMIT ?,?";

        try {
            conexao = ConexaoBD.obterConexao();
            stmt = conexao.prepareStatement(sql);
            stmt.setInt(1, (pagina - 1) * ItemProduto.TAMANHO_PAGINA);
            stmt.setInt(2, ItemProduto.TAMANHO_PAGINA);
            rs = stmt.executeQuery();
            
            while (rs.next()) {
                ItemProduto produto = new ItemProduto();
                produto.setIdProduto(rs.getInt("id_produto"));
                produto.setNomeProduto(rs.getString("nome_produto"));
                produto.setPrecoProduto(rs.getBigDecimal("preco_produto"));
                listaProdutos.add(produto);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            ConexaoBD.fechar(rs, stmt, conexao);
        }
        return listaProdutos;
    }

    /**
     * Conta o total de registros
     *
     * @return Contagem total de registros
     */
    public int contarTodosProdutos() {
        int total = 0;
        Connection conexao = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        String sql = "SELECT COUNT(*) FROM produto";

        try {
            conexao = ConexaoBD.obterConexao();
            stmt = conexao.prepareStatement(sql);
            rs = stmt.executeQuery();
            
            if (rs.next()) {
                total = rs.getInt(1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            ConexaoBD.fechar(rs, stmt, conexao);
        }
        return total;
    }
}

O método buscarTodosProdutos(int pagina) busca produtos de uma página específica. O parâmetro pagina indica qual página recuperar. O método contarTodosProdutos() obtém o total de registros. Isso permite saber quantas páginas serão necessárias.

Uma limitação desse método é que, quendo novos registros são inseridos, alguns itens podem não ser exibidos em consultas subsequentes, o que requer otimização.

Criamos a classe ListagemProdutos, um Servlet que trata as requisições de paginação de produtos. O método doGet() processa as solicitações de paginação.

package controle;

import modelo.ItemProduto;
import repositorio.RepositorioProduto;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet("/ListagemProdutos")
public class ListagemProdutos extends HttpServlet {
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        // Lógica para requisições POST
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        int paginaAtual = 1; // Página atual
        if (request.getParameter("pagina") != null) {
            paginaAtual = Integer.parseInt(request.getParameter("pagina"));
        }
        
        RepositorioProduto repositorio = new RepositorioProduto();
        List<ItemProduto> listaProdutos = repositorio.buscarTodosProdutos(paginaAtual);
        request.setAttribute("listaProdutos", listaProdutos);
        
        int totalPaginas; // Total de páginas
        int totalRegistros = repositorio.contarTodosProdutos();
        
        if (totalRegistros % ItemProduto.TAMANHO_PAGINA == 0) {
            totalPaginas = totalRegistros / ItemProduto.TAMANHO_PAGINA;
        } else {
            totalPaginas = totalRegistros / ItemProduto.TAMANHO_PAGINA + 1;
        }
        
        StringBuilder barraNavegacao = new StringBuilder();
        for (int i = 1; i <= totalPaginas; i++) {
            if (i == paginaAtual) {
                barraNavegacao.append("[").append(i).append("]");
            } else {
                barraNavegacao.append("<a href='ListagemProdutos?pagina=").append(i)
                             .append("'>").append(i).append("</a>");
            }
        }
        barraNavegacao.append("  ");
        request.setAttribute("barraNavegacao", barraNavegacao.toString());
        
        request.getRequestDispatcher("exibir_produtos.jsp").forward(request, response);
    }
}

A classe ListagemProdutos realiza duas tarefas principais: obter o resultado da consulta paginada e construir a barra de navegação. A consulta paginada é implementada chamando o método buscarTodosProdutos(pagina) da classe RepositorioProduto. A barra de navegação é construída concatenando strings HTML e, em seguida, usando o objeto HttpServletRequest, o request é encaminhado para a página exibir_produtos.jsp para exibição.

Criamos a página exibir_produtos.jsp, que utiliza expressões EL e APIs Java para exibir os resultados da consutla e a barra de navegação.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>Lista de Produtos</title>
</head>
<body>
| Informações dos Produtos ------------------------ |
|:-:|
| ID | Nome do Produto | Preço |
<foreach items="${requestScope.listaProdutos}" var="produto"> | ${produto.idProduto} | ${produto.nomeProduto} | ${produto.precoProduto} |
 </foreach>| &lt;%=request.getAttribute("barraNavegacao")%&gt; |
</body>
</html>

Finalmente, apresentamos a classe utilitária ConexaoBD que encapsula a conexão com o banco de dados JDBC:

package util;

/**
 * Utilitário para conexão com banco de dados
 */

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class ConexaoBD {

    /**
     * Obtém conexão com o banco
     *
     * @return Conexão
     * @throws Exception Exceção
     */
    public static Connection obterConexao() throws Exception {
        // Nome do driver JDBC
        String nomeDriver = "com.mysql.jdbc.Driver";
        Class.forName(nomeDriver);
        
        // URL do banco de dados
        String urlBanco = "jdbc:mysql://servidor:3306/banco?useUnicode=true&characterEncoding=UTF-8";
        
        // Usuário do banco
        String usuario = "usuario";
        
        // Senha do banco
        String senha = "senha";
        
        Connection conexao = DriverManager.getConnection(urlBanco, usuario, senha);
        return conexao;
    }

    /**
     * Fecha conexão
     *
     * @param stmt Statement
     * @param conn Connection
     * @throws Exception Exceção
     */
    public static void fechar(Statement stmt, Connection conn) throws Exception {
        if (stmt != null) {
            stmt.close();
            if (conn != null) {
                conn.close();
            }
        }
    }

    /**
     * Fecha conexão
     *
     * @param cstmt CallableStatement
     * @param conn Connection
     * @throws Exception Exceção
     */
    public static void fechar(CallableStatement cstmt, Connection conn) throws Exception {
        if (cstmt != null) {
            cstmt.close();
            if (conn != null) {
                conn.close();
            }
        }
    }

    /**
     * Fecha conexão
     *
     * @param pstmt PreparedStatement
     * @param conn Connection
     * @throws SQLException Exceção SQL
     */
    public static void fechar(PreparedStatement pstmt, Connection conn) {
        if (pstmt != null) {
            try {
                pstmt.close();
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Método sobrecarregado para fechar conexão
     *
     * @param rs ResultSet
     * @param pstmt PreparedStatement
     * @param conn Connection
     * @throws Exception Exceção
     */
    public static void fechar(ResultSet rs, PreparedStatement pstmt, Connection conn) {
        if (rs != null) {
            try {
                rs.close();
                if (pstmt != null) {
                    pstmt.close();
                    if (conn != null) {
                        conn.close();
                    }
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

Tags: jdbc MySQL paginação java banco de dados

Publicado em 6-17 05:26