Francisco Javier Martínez Páez

Consultor tecnológico de desarrollo de proyectos informáticos.

 Ingeniero Técnico en Telecomunicaciones

Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación

Somos expertos en Java/J2EE

Ver todos los tutoriales del autor

Fecha de publicación del tutorial: 2006-09-26

Tutorial visitado 26.516 veces Descargar en PDF
JDBC1

PAGINAR UN RESULTSET

Los ejemplos de este tutorial están hechos con el siguiente entorno de desarrollo:

  • Jboss Eclipse IDE Milestone 5.

  • JDK 1.4

  • MySQL 5.0

  • MySQL Administrator (opcional)

  • MySQL Connector/J (Driver tipo 4 que implementa la versión JDBC 3.0)

¿ JDBC ?

Aunque este tutorial está orientado a gente con algún conocimiento de JDBC, no está de más realizar una pequeña introducción, para refrescar ciertos conceptos.

El API de JDBC está expresado como un conjunto de interfaces abstractos que nos permiten principalmente:

  • Manejar un conjunto de Drivers JDBC

  • Abrir conexiones a la base de datos

  • Ejecutar sentencias SQL.

  • Procesar los resultados obtenidos.

Será por tanto cuestión de cada fabricante implementar dicha funcionalidad en los drivers.

La relación de los Interfaces principales es la siguiente:

Relación de los Interfaces


Y ya sin más dilación, y dando por supuesto que se manejan con cierta soltura estas clases, vamos a currar un poquito.

PAGINANDO

Pocas aplicaciones WEB no tienen una página que muestra de manera paginada los resultados de una consulta. Cuando nos disponemos a decidir cual es la mejor manera de paginar (o mejor dicho) donde paginar, surgen ciertas dudas al respecto. Yo siempre he creido (y si el driver lo permite) que la mejor manera de paginar es en el ResultSet, y no posteriormente en alguna lista o colección. Alguno me preguntará que porqué pienso así, la respuesta es simple, si paginamos en el ResultSet, no es necesario transmitir por la red todos los registros obtenidos, sino sólo los estrictamente necesarios. Hay una manera mejor que esta, y es usar alguna carácterística de nuestro motor de base de datos por ejemplo, el rownum de Oracle, pero esto no se incluye en todos los motores de base de datos.

Lo primero que vamos a hacer es crearnos una tabla en nuestra base de datos:

Usando la herramienta MySQL Administrator, nos creamos la tabla USUARIOS, con los siguientes campos:

Creando la tabla con MySQLAdministrator


Una vez creada la tabla, podemos abrir el eclipse.

Nos generamos un proyecto nuevo (yo le he llamado JDBC). Recordad que debemos meter en el classpath el driver JDBC de mysql:

Incluir en el classpath el driver de MySQL

Creamos la clase que nos va a servir de ejemplo: La denominamos Paginando: (le decimos que nos genere el método main que despues rellenaremos)

Creando la clase

Vamos a crear nuestro primer método. getConnection() y dos constantes que se usarán dentro del método:

// Driver de MySQL
        protected static String dbClass = "com.mysql.jdbc.Driver";

// Cadena de conexión
        protected static String dbUrl = "jdbc:mysql:///paco"; 

// Método que crea una conexión a nuestra Base de datos

protected Connection getConnection() {      

        Properties props = new Properties();
        props.put("user","tu_usuario");
        props.put("password","tu_clave");       
        Connection conBBDD = null;
        try {           
            Class.forName(dbClass);
            conBBDD=DriverManager.getConnection(dbUrl, props);
        } catch(Exception e) {
            return null;
        }     

        return conBBDD;
         }

Nos vamos a crear un método para rellenar con datos nuestra tabla, que nos servirá también para comprobar que funciona el método getConnection().

protected void insertaRegistros() {       

        Connection conn = getConnection();       
        Statement st=null;
        PreparedStatement ps=null;
        try {
            st = conn.createStatement();
            st.executeUpdate("delete from usuarios");
        } catch (SQLException e) {           
            e.printStackTrace();           
        }   
       
        try {
     ps = conn.prepareStatement("INSERT INTO USUARIOS VALUES         (?,?)");           
            for(int i=0;i<110;i++) {               
                ps.setString(1,"Nombre "+i);
                ps.setInt(2,i);               
                ps.executeUpdate();               
            }           
        } catch (SQLException e) {           
            e.printStackTrace();
        } finally {
            if(st!=null) {
                try {
                    st.close();
                } catch (SQLException e) {               
                    e.printStackTrace();
                }
            }
            if(ps!=null) {
                try {
                    ps.close();
                } catch (SQLException e) {                   
                    e.printStackTrace();
                }
            }
           
            if(conn!=null) {
                try {
                    conn.close();
                } catch (SQLException e) {                   
                    e.printStackTrace();
                }               
            }
        }             
               
    }

Lo invocamos desde el método main().

public static void main(String[] args) {   
            Paginando pag = new Paginando();
            pag.insertaRegistros();
        }

Comprobamos usando MySQLAdministrator los datos insertados:

Datos en la tabla

Ya tenemos datos suficientes en la tabla para nuestro ejemplo.

Vamos ahora a crear el código para paginar.  Creamos un método que llamaremos ejecutaQueryPaginada. Este método recibe:

  • String query: Consulta SQL a lanzar
  • int numPagina: número de página que queremos consultar
  • int numRegPagina: número de registros por página.

Este método retorna una lista de la siguiente manera:

  • El primer elemento será un Integer con el número de elementos totales en la tabla que cumplen la query.
  • El segundo elemento será una lista de Strings con los nombres de las columnas consultadas.
  • A continuación devolverá listas de Strings con los valores correspondientes a la página buscada.

public ArrayList ejecutaQueryPaginada(String query, int numPagina, 
                        int numRegPagina) {   

    ArrayList alResultado = new ArrayList();
    Connection conBBDD = null;
    Statement st = null;
    ResultSet rs = null;
    try {                   
        conBBDD=getConnection();                   
        st = conBBDD.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
        st.setFetchSize(numRegPagina);
       
        rs = st.executeQuery(query);

        int fila = numRegPagina * (numPagina - 1) + 1;
        int cont = 1;

        ResultSetMetaData md = rs.getMetaData();
        int numeroColumnas = md.getColumnCount();
        ArrayList alRegistro = new ArrayList(numeroColumnas);

   
        for (int i = 1; i <= numeroColumnas; i++) {
            String nomCol = md.getColumnName(i);
            alRegistro.add(nomCol);
        }

        alResultado.add(alRegistro);

        if (rs.absolute(fila) && numRegPagina > 0) {
            do {
                alRegistro = new ArrayList();
                for (int i = 1; i <= numeroColumnas; i++) {
                    alRegistro.add(rs.getString(i));
                }

                alResultado.add(alRegistro);
                cont++;
               
            }
            while (rs.next() && (cont <= numRegPagina));
        }
        // Se incluye el primer elemento del ArrayList con un objeto Integer
        // con el Numero de Tuplas TOTAL de la query paginada
        // Se mueve el cursor a ultima tupla
        Integer numTuplasTotal = new Integer(0);
        if (rs.last()) { // Existen tuplas y el cursor esta en la ultima,
                            // basta con consultar el numero de esa tupla
            numTuplasTotal = new Integer(rs.getRow());
        }
        alResultado.add(0, numTuplasTotal);       
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if(st!=null) {
            try {
                st.close();
            } catch (SQLException e) {           
                e.printStackTrace();
            }
        }
        if(rs!=null) {               
            try {
                rs.close();
            } catch (SQLException e) {           
                e.printStackTrace();
            }
        }
        if(conBBDD!=null) {
            try {
                conBBDD.close();
            } catch (SQLException e) {               
                e.printStackTrace();
            }
           
        }
    }

    return (alResultado);

}


Se ha resaltado ResultSet.TYPE_SCROLL_INSENSITIVE, porque para que nuestro código no genere un error, el driver de base de datos nos debe permitir generar "ResultSets scrollables", es decir, ResultSets que nos permitan navegar por el hacia adelante y hacia atrás.

Vamos a invocar el método desde main():

public static void main(String[] args) {       

        Paginando pag = new Paginando();       
        String consulta = "SELECT * FROM USUARIOS  ORDER BY EDAD";       
        List lista = pag.ejecutaQueryPaginada(consulta,1,10);
       
        Integer numeroRegistros = (Integer)lista.get(0);
       
        System.out.println("NUMERO DE REGISTROS TOTALES:"+numeroRegistros.toString());
       
        for(int i=1;i<lista.size();i++) {
            List lista1 = (List) lista.get(i);   
            System.out.println(lista1.toString());
        }

    }

Hemos solicitado los 10 primeros usuarios más jóvenes: (pagina 1/10)

Lo ejecutamos y mostramos el resultado en la consola:

Resultado

Vamos a solicitar la página 5:

List lista = pag.ejecutaQueryPaginada(consulta,5,10);

Lo ejecutamos y mostramos el resultado en la consola:

Resultado

Vamos a solicitar la página 1 devolviendo 25 registros por página:

List lista = pag.ejecutaQueryPaginada(consulta,1,25);

Lo ejecutamos y mostramos el resultado en la consola:

Resultado

Puedes seguir ejecutando las pruebas que quieras.

Podrías mejorar el código usando PreparedStatement en lugar de Statement  para evitar SQLInjection. Necesitarías entonces un método para sustituir en el PreparedStatement los valores adecuados de manera dinámica. Pero eso lo dejo a tu imaginación...

En posteriores tutoriales publicaré uno acerca de las nuevas características que incorpora JDBC en Java 5 donde hablaremos de CachedRowSet, y verás que fácil es paginar...

Ya sabéis que si necesitáis ayuda http://www.autentia.com


A continuación puedes evaluarlo:

Regístrate para evaluarlo

Por favor, vota +1 o compártelo si te pareció interesante

Share |
Anímate y coméntanos lo que pienses sobre este TUTORIAL:

Fecha publicación: 2010-04-28-01:17:25

Autor: jkintero

Hola he utilizado su tutorial para paginar y me ha funcionado de maravilla. Solo que tengo una duda.

Si tengo miles de registros que tan optimo es paginar el resulset? es mejor por query ?, antes lo realizaba por query y lo creia mas optimo pero tenia broncas para ordenar,asi lo realizaba:

select * from (select rownum,nombre,apellido,edad from clientes where fecha between '01/01/2008' and '01/01/2009') a where between a.rownum = 40 and a.rownum=60

Fecha publicación: 2008-01-23-01:13:19

Autor:

[Sergio Vallejo] Si estas paginando un ResultSet y este contiene un tipo BLOB al hacer un rs.getString(i) da una excepcion esto se puede solucionar de la siguiente manera: if( rs.getObject(i)!= null && rs.getObject(i).getClass().getName().equalsIgnoreCase("oracle.sql.BLOB")){ alRegistro.add(rs.getBlob(i)); }else{ alRegistro.add(rs.getString(i)); }

Fecha publicación: 2008-01-23-01:13:19

Autor:

[Sergio Vallejo] Si estas paginando un ResultSet y este contiene un tipo BLOB al hacer un rs.getString(i) da una excepcion esto se puede solucionar de la siguiente manera: if( rs.getObject(i)!= null && rs.getObject(i).getClass().getName().equalsIgnoreCase("oracle.sql.BLOB")){ alRegistro.add(rs.getBlob(i)); }else{ alRegistro.add(rs.getString(i)); }

Fecha publicación: 2006-12-24-03:16:36

Autor:

[roger padilla] muy bacano man, gracias por regalarnos parte de tus amplios conocimeintos.