Crear un paginador utilizando JSTL Core.
0. Índice de contenidos.
- 1. Introducción.
- 2. Entorno.
- 3. Nuestro ejemplo.
- 4. El controlador.
- 5. La pantalla de consulta.
- 6. El paginador.
- 7. Referencias.
- 8. Conclusiones.
1. Introducción
Existen infinidad de aplicaciones en las que el usuario consulta datos y el sistema se los devuelve (el buscador de google sin ir más lejos). Normalmente, lo que hacen las aplicaciones no es devolver todos los resultados que cumplen con los criterios de filtrado, sino devolver los resultados en páginas.
El motivo de devolver los resultados en páginas es, principalmente, la eficiencia. No es lo mismo hacer una consulta a la base de datos que devuelva 3000 resultados, que devolver los 10 primeros, o los elementos del 180 al 190.
En este tutorial vamos a ver cómo implementar nuestro propio paginador de resultados de consultas utilizando el taglib JSTL Core, el núcleo de la librería de etiquetas standard para JSP.
2. Entorno.
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro 15′ (2.2 Ghz Intel Core I7, 4GB DDR3).
- Sistema Operativo: Mac OS Snow Leopard 10.6.7
- Entorno de desarrollo: Eclipse 3.6.2.
- Apache Tomcat 6.0.32 con jdk 1.6.
- Navegador: Mozilla Firefox 4.0.1
3. Nuestro ejemplo.
Nuestro ejemplo consistirá en una pantalla para consultar datos, en concreto nombres de ciudades (por ejemplo…).
El sistema devolverá la cantidad total (un número) de todas las ciudades que tiene almacenadas y los resultados en páginas. Lo que es lo mismo, si hubiese un tamaño de página fijo de, por ejemplo 10 elementos, la primera página devolvería los 10 primeros resultados, la segunda página los elementos del 11 al 20, la tercera del 21 al 30, etc…
4. El controlador.
En nuestro ejemplo, el controlador será el encargado de recibir y tramitar las peticiones de consulta por parte de los usuarios.
Para ello hemos creado un Servlet (también valdría un Action de Struts o un Controller de Spring, etc…) que recibe un único parámetro. Este parámetro será el offset, o lo que es lo mismo, el desplazamiento con el que tiene que devolver los resultados que va a consultar.
El elemento offset combinado con el máximo de elementos por página conformarán cada una de las páginas.
Por ejemplo, si el offset es 0 y el máximo número de elementos por página es 10, significa que el usuario quiere que se le devuelvan los elementos del 1 al 10. Si el offset es 2 y el máximo de elementos por página 10, significa que el usuario está pidiendo los resultados del 21 al 30. Un truco, el primer elemento a devolver sería: offset * máximo número de elementos por página + 1.
En nuestro ejemplo, el offset podría interpretarse como el número de página (la primera página sería la 0).
Por tanto, cada vez que nuestro controlador reciba una petición hará dos cosas: obtener el número total de elementos y obtener los elementos relativos al offset que recibe (o lo que es lo mismo, devuelve la página solicitada).
El servlet quedaría de la siguiente manera:
import java.util.List; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ConsultasServlet extends HttpServlet { // MAXIMO NÚMERO DE ELEMENTOS POR PÁGINA private static final Integer MAX_ELEMENTOS_PAGINA = 10; public void doGet(HttpServletRequest request, HttpServletResponse response){ int offset; try { // parámetro que nos indicaría la página (0 es la primera, 1 la segunda...) offset = Integer.valueOf(request.getParameter("offset")); } catch (NumberFormatException e) { offset = 0; } // consultamos y almacenamos los resultados para que puedan ser manejados posteriormente request.setAttribute("elements", getElementosPagina(offset, MAX_ELEMENTOS_PAGINA)); request.setAttribute("maxElements", MAX_ELEMENTOS_PAGINA); request.setAttribute("totalElements", getTotalElementos()); request.setAttribute("offset", offset + 1); // offset + 1. La vista lo interpretará como la página try { // nos vamos a la vista getServletConfig().getServletContext().getRequestDispatcher("/consulta.jsp").forward(request,response); } catch (Exception e) { e.printStackTrace(); } } private List<String> getElementosPagina (int offset, int maxElementosPagina) { // esto devolvería una lista de elementos en función del offset y del máximo de elementos por página } private int getTotalElementos () { // esto devolvería el número total de elementos del sistema } }
5. La pantalla de consulta.
En nuestro caso, la pantalla de consulta será una jsp que «pintará» los resultados devueltos por el controlador y que pasará al paginador la información (parámetros) necesaria para que funcione correctamente.
Quedaría de la siguiente manera:
<!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=ISO-8859-1"> <title>Prueba de paginador</title> <style type="text/css"> body { font-family:verdana,sans-serif; } div.paginator { margin: 10px 0; } div.paginator span.elements_found { float: left; font-size: 0.85em; } div.paginator span.pages { float: right; } div.paginator span.pages span.currentPage { font-weight: bold; } div.paginator span.pages a { padding: 2px; } div.paginator span.invisible { visibility: hidden; } </style> </head> <body>AUTENTIA - EJEMPLO DE PAGINADOR CON JSTL
<table> <tr> <td></td> </tr> </table> <jsp:param name="maxElements" value="${requestScope.maxElements}"/> <jsp:param name="totalElements" value="${requestScope.totalElements}"/> <jsp:param name="offset" value="${requestScope.offset}"/> <jsp:param name="maxPagesDisplayed" value="7"/> <input type="hidden" name="offset"/> </body> </html>
Observamos que la jsp «pinta» en una tabla los resultados de la consulta. Posteriormente, incluye la jsp del paginador, a la que le pasa 4 parámetros que necesitará para funcionar correctamente:
- maxElements: lo obtiene de la request (ver servlet). Indica el máximo número de elementos que tiene cada página.
- totalElements: lo obtiene de la request (ver servlet). Indica el número total de elementos encontrados (¡no el número de elementos de la página!).
- offset: lo obtiene de la request (ver servlet). La página actual.
- maxPagesDisplayed: valor constante. Indica el máximo número de accesos rápidos a páginas que tendrá el paginador.
Como se puede observar, al final hay una función javascript, a la que invocará el paginador para cambiar de página. Envía un formulario con el offset al servidor.
6. El paginador.
El paginador nos permitirá navegar a través de los resultados de consulta enviando peticiones al servidor para que éste devuelva páginas. Lo hemos implementado en una jsp (paginador.jsp) para que pueda ser tratado como un componente independiente, reutilizable en otras páginas de consulta.
Las características que deberá tener nuestro paginador serán las siguientes:
- Debe mostrar la cantidad total de elementos encontrados (el número, no los elementos).
- Debe indicar qué elementos se están mostrando.
- Debe proporcionar un acceso rápido a un número determinado de páginas. En nuestro ejemplo a 6 páginas más la actual (total 7).
- En caso de que el número total de páginas sea superior a 7 (ej. 250 resultados a 10 resultados por página serían 25 páginas), debe proporcionar un acceso rápido a la página anterior (a las 7 mostradas), a la siguiente, al inicio y a la última.
El código sería este:
<c:set var="MAX_ELEMENTS" scope="page" value="${! empty param['maxElements'] ? requestScope.maxElements : 10}"/> <c:set var="TOTAL_ELEMENTS" scope="page" value="${param['totalElements']}"/> <c:set var="CURRENT_PAGE" scope="page" value="${! empty param['offset'] ? param['offset'] : 1 }"/> <c:set var="MAX_PAGES_DISPLAYED" scope="page" value="${! empty param['maxPagesDisplayed'] ? param['maxPagesDisplayed'] : 7 }"/><c:out value="${pageScope.TOTAL_ELEMENTS}"/> elementos encontrados. Mostrando del <c:out value="${(pageScope.CURRENT_PAGE - 1) * pageScope.MAX_ELEMENTS + 1}"/> al <c:out value="${pageScope.CURRENT_PAGE * pageScope.MAX_ELEMENTS >= pageScope.TOTAL_ELEMENTS ? pageScope.TOTAL_ELEMENTS : pageScope.CURRENT_PAGE * pageScope.MAX_ELEMENTS}"/> <c:set var="TOTAL_PAGES" value="${1 + ((pageScope.TOTAL_ELEMENTS - (pageScope.TOTAL_ELEMENTS mod pageScope.MAX_ELEMENTS)) div pageScope.MAX_ELEMENTS)}" scope="page" /> <c:set var="TOTAL_PAGES" value="${pageScope.TOTAL_ELEMENTS div pageScope.MAX_ELEMENTS}"/> <c:set var="firstPageDisplayed" value="1" scope="page"/> <c:set var="lastPageDisplayed" value="${pageScope.TOTAL_PAGES}" scope="page"/> <c:set var="firstPageDisplayed" value="${pageScope.CURRENT_PAGE - (pageScope.MAX_PAGES_DISPLAYED div 2 - (pageScope.MAX_PAGES_DISPLAYED mod 2) / 2)}" scope="page"/> <c:set var="lastPageDisplayed" value="${pageScope.firstPageDisplayed + pageScope.MAX_PAGES_DISPLAYED - 1}" scope="page"/> <c:set var="firstPageDisplayed" value="1" scope="page"/> <c:set var="lastPageDisplayed" value="${pageScope.MAX_PAGES_DISPLAYED}" scope="page"/> <c:set var="firstPageDisplayed" value="${pageScope.TOTAL_PAGES - pageScope.MAX_PAGES_DISPLAYED + 1}" scope="page"/> <c:set var="lastPageDisplayed" value="${pageScope.TOTAL_PAGES}" scope="page"/> <c:out value="${counter}"/> <c:out value="${counter}"/> 1 --%> No se encontraron elementos
Aquí se puede ver cómo quedaría:
7. Referencias.
8. Conclusiones.
En este tutorial hemos visto la importancia de realizar búsquedas páginadas para mejorar la eficiencia. El paginador es un componente fundamental en esta tarea.
Hemos propuesto la solución de implementar nuestro propio paginador con la ayuda de la librería de etiquetas JSTL Core en una jsp. Además, esta jsp la podemos utilizar en cualquier página de consultas.
Por último, me gustaría añadir que esta es solo una solución más para resolver el problema de la paginación, pero existen otras soluciones. Os invito a que investiguéis sobre el taglib display tag, que también nos ofrece un mecanismo de paginación. Seguro que os interesa.
Espero que este tutorial os haya sido de ayuda. Un saludo.
Miguel Arlandy
Hola Miguel, he usado tu codigo para crear una paginacion y no me salen todos los enlaces de las paginas.
¿Está el codigo completo de la jsp?
Un saludo.
Hola gracias por el ejemplo estimado puedes ayudarme en lo siguiente
de esta forma estoy mostrando la clave en la base de datos
Password:
el problema es que la tengo encriptada en la base y estoytratando de hacer algo asi
Password:
no logro hacer que funcione podrias ayudarme?
en la actualizacion tambien tengo el mismo problema
<sql:param value='’ />