JSTL 1.0. Estandarizando JSP.

Un sujeto de controversa es la inclusin en JSTL de acciones para acceder a bases de datos. Alguna gente ve esto como una mala prctica y argumenta que todos los accesos a bases de datos se deberan realizar desde componentes Java puros en una aplicacin basada en MVC en lugar de desde pgina JSP. Estoy de acuerdo en este punto de vista para cualquier cosa que no sean aplicaciones sencillas, pero hay muchas aplicaciones que se cualifican como muy simples y donde el tiempo de desarrollo o las habilidades necesarias hacen impracticable la arquitectura MVC. Sin el soporte de JSTL, estas aplicaciones normalmente terminan con el cdigo para acceder a la base de datos dentro de scriptles, lo que es peor todava para un mantenimiento y desarrollo correctos. Por lo tanto, veremos como acceder a bases de datos desde pginas JSP, pero te pediremos que tengas en mente que esta aproximacin no es la mejor para todos los tipos de aplicaciones. Si tu equipo de desarrollo incluye programadores Java, deberas considerar seriamente encapsular el cdigo de acceso a la base de datos en clases Java, y utilizar las JSP slo para mostrar los resultados.

Las acciones de bases de datos de JSTL estn basadas en el API JDBC de Java y usan las abstraccion javax.sql.DataSource presentada en JDBC 2.0 para representar una base de datos. Un DataSource proporciona conexiones a una base de datos y puede implementar una caracterstica llamada almacen de conexiones. Abrir una conexin fsica a una base de datos es una operacin que consume mucho tiempo. Con el almacenamiento de conexiones, slo necesitamos hacerlo una vez, y la misma conexin se puede reutilizar una y otra vez, sin los riesgos de problemas asociados con otras aproximaciones de comparticin de conexiones.

.Poner un objeto DataSource a Disposicin de JSTL

JSTL soporta varias formas para hacer que un objeto DataSource est disponible para las acciones de bases de datos. En un contenedor Web con soporte JNDI (Java Naming and Directory Interface), se puede definir un DataSource por defecto como un recurso JNDI con un parmetro de contexto en el fichero web.xml:

<context-param>
<param-name>
  javax.servlet.jsp.jstl.sql.dataSource
</param-name>
<param-value>
  jdbc/Production
</param-value>
</context-param>

Se deben utilizar las herramientas de configuracin JNDI del contenedor WEB para configurar un recurso JNDI con el nombre especificado; por ejemplo, con un nombre de usuario y una password de una cuenta de usuario en una base de datos, las conexiones mnimas y mximas en el almacen, etc. La forma de realizar esto vara entre los contenedores y est fuera del mbito de este artculo.

Una alternativa para los contenedores que no soportan JNDI es que un oyente del ciclo de vida de la aplicacin (contexto del servlet) cree y configure un DataSource y lo seleccione como el valor por defeto usando la clase Config de JSTL:

import javax.servlet.*;
import javax.servlet.http.*;
import oracle.jdbc.pool.*;

public class AppListener implements ServletContextListener {

    private OracleConnectionCacheImpl ds =null;

    public void contextInitialized(ServletContextEvent sce){
        ServletContext application =sce.getServletContext();

        try {
            ds = new OracleConnectionCacheImpl();
            ds.setURL("jdbc:oracle:thin:@voyager2:1521:Oracle9i");
            ds.setMaxLimit(20);
            ds.setUser("scott");
            ds.setPassword("tiger");
        }
        catch (Exception e){
            application.log("Failed to create data source:"+ e.getMessage());
        }
        Config.set(application, Config.SQL_DATASOURCE, ds);
    }
...
}

La clase oyente de este ejemplo crea un DataSource con capacidades de almacen de conexiones para un base de datos Oracle9i, y la hace disponible como por defecto para las acciones JSTL usando la clase Config para seleccionar la variable de configuracin correspondiente.

Una tercera forma, slo disponible para prototipos o aplicaciones que no van a utilizarse tan duramente como para necesitar el almacen de conexiones, es usar la accin <sql:setDataSource>:

<sql:setDataSource
url="jdbc:mysql://dbserver/dbname"
driver="org.gjt.mm.mysql.Driver"
user="scott"
password="tiger" />

Esta accin crea una sencilla fuente de datos, sin almacenamiento, para la URL JDBC especificada, con el usuario y la password, usando el driver JDBC especificado. Podramos usar esta accin para empezar, pero recomendamos la utilizacin de una de las otras alternativas para una site del mundo real. Adems de privarnos del almacen de conexiones para una fuente de datos creada de esta forma, no es una buena idea incluir informacin sensible como la URL de la base de datos, el nombre de usuario y la password en una pgina JSP, ya que sera posible para alguien acceder al cdigo fuente de la pgina.

.Leer Datos de la Base de Datos

Con una DataSource a nuestra disposicin, podemos acceder a la base de datos. Aqu podemos leer datos desde una base de datos representada por el DataSource por defecto:

<%@ taglib prefix="sql" uri="http://java.sun.com/jstl/sql" %>

<html>
<body>
  <h1>Reading database data</h1>
  <sql:query var="emps" sql="SELECT * FROM Employee" />
  ...
</body>
</html>

Primero necesitamos declarar la librera JSTL que contiene las acciones de bases de datos, usando la directiva taglib de la parte superior de este ejemplo. La accin <sql:query> ejecuta la sentencia SQL SELECT especificada por el atributo sql (o como el cuerpo del elemento accin) y graba los resultados en una variable nombrada por el atributo var.

El resultado de la consulta a la base de datos se devuelve como un bean del tipo javax.servlet.jsp.jstl.sql.Result con varias propiedades de slo lectura:

Propiedad Tipo Java Descripcin
rows java.util.SortedMap[] Un array con un mapa insensible a las maysculas por cada fila con las claves correspondiendo con los nombres de columna y valores correspondiendo con los valores de columna.
rowsByIndex Object[][] Un array con un array por fila con valores de columna.
columnNames String[] Un array con nombres de columnas
rowCount int El nmero de filas en el resultado.
limitedByMaxRows boolean true si no se han incluido todas las filas debido a que se ha alcanzado el lmite mximo de filas especificado.

Ya vimos como utilizar la accin <c:forEach> para mostrar todas o slo algunas filas en la primera pgina de este tutorial, por eso ahora veremos como podemos obtener slo algunas filas para mostrarlas todas en esta parte. Los enlaces Next y Previous le permiten al usuario solicitar un conjunto diferente. Primero, aqu est cmo leer un subconjunto de filas y luego mostrar el subconjunto completo:

<c:set var="noOfRows" value="10" />

<sql:query var="emps" 
startRow="${param.start}" maxRows="${noOfRows}">
SELECT * FROM Employee
</sql:query>

<ul>
<c:forEach items="${emps.rows}" var="${emp}">
  <li><c:out value="${emp.name}" />
</c:forEach>
</ul>

El atributo startRow para la accin <sql:query> se enva a una expresin EL que lee el valor de un parmetro de solicitud llamado start. Pronto veremos como cambia este valor cuando se pulsa sobre los enlaces Next y Previous. La primera vez que se accede a la pgina, el parmetro no existe, por eso la expresin se evala a 0. Esto significa que el resultado de la consulta contiene filas empezando con la primera que corresponda (ndice 0). El atributo maxRows lmita el nmero total de filas del valor de la variable noOfRows, lo seleccionamos a 10 en este ejemplo. La accin <c:forEach> hace un bucle sobre todas las columnas del resultado y genera una lista de tems con los valores de columna por cada fila.

Tambin debemos generar los enlaces Next y Previous para permitir que el usuario seleccione un nuevo conjunto de filas:

<c:choose>
<c:when test="${param.start > 0}">
  <a href="emplist.jsp?start=<c:out 
	value="${param.start - noOfRows}"/>">Previous Page</a>
</c:when>
<c:otherwise>
  Previous Page
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${emps.limitedByMaxRows}">
  <a href="emplist.jsp?start=<c:out
	value="${param.start + noOfRows}"/>">Next Page</a>
</c:when>
<c:otherwise>
  Next Page
</c:otherwise>
</c:choose>

El primer bloque <c:choose> es idntico al de la pgina 1; si el parmetro de la solicitud start es mayor que cero, la pgina actual muestra un subconjunto de filas distinto del primero, por eso se aade un enlace Previous. El enlace apunta hacia la misma pgina, e incluye el parmetro start con un valor que es el valor actual menos el nmero de filas mostrado por cada pgina.

El segundo bloque <c:choose> se aprovecha de la propiedad limitedByMaxRows del resultado de la consulta. Si esta propiedad es true, significa que se ha truncado el resultado al nmero de filas mostrado por cada pgina. De aqu, se genera el enlace Next con valor del parmetro start para el nuevo subconjunto de filas.

.Escribir Datos en la Base de Datos

Adems de leer datos desde una base de datos, tambin podemos usar JSTL para actualizar informacin. Este ejemplo muestra cmo insertar una nueva fila en una tabla:

<c:catch var="error">
<fmt:parseDate var="empDate" value="${param.empDate}" 
  pattern="yyyy-MM-dd" />
</c:catch>
<c:if test="${error != null}">
<jsp:useBean id="empDate" class="java.util.Date" />
</c:if>

<sql:update>
INSERT INTO Employee (FirstName, LastName, EmpDate)
  VALUES(?, ?, ?)
<sql:param value="${param.firstName}" />
<sql:param value="${param.lastName}" />
<sql:dateParam value="${empDate}" type="date" />
</sql:update>

Antes de insertar la fila, este ejemplo ilustra cmo usar las acciones de validacin JSTL, como prometimos anteriormente. La pagina espera que todos los datos para la nueva fila se enven como parmetros de solicitud (quizs introducidos en un formulario HTML), incluyendo una fecha de contratacin. antes de que la fecha se pueda insertar en la base de datos, debemos convertirla a su forma nativa Java. Esto es lo que hace la accin <fmt:parseDate>. El atributo value contiene una expresin EL que obtiene el valor del parmatro de solicitud empDate. Al accin trata de interpretarlo como una fecha escrita en el formato especificado por el atributo pattern (un ao de cuatro dgitos, seguido por dos dgitos del ms y dos dgitos del da, separados por barras inclinadas). Si tiene xito, almacena la fecha en su forma nativa con el nombre especificado por el atributo var

La accin <c:catch> tiene en cuenta los strings invlidos. Si el valor del parmetro no puede ser interpretado como una fecha, el <fmt:parseDate> lanza una excepcin, que la accin <c:catch> captura y graba en la variable especificada. Cuando esto sucede, la condicin de comprobacin de la accin <c:if> se evala a true, por lo que fecha de contratacin es creada por la accin <jsp:useBean> anidada.

Para insertar la fila, usamos la accin <sql:update>. Como la accin de consulta, la sentencia SQL se puede especificar como el cuerpo del elemento o mediante un atributo sql. La accin <sql:update> se puede usar para ejecutar sentencias INSERT, UPDATE, DELETE, asi como sentencias para crear o eliminar objetos en la base de datos, como CREATE TABLE y DROP TABLE. El nmero de filas afectado por la sentencia puede capturarse de forma opcional en una variable nombrada por el atributo var.

En este ejemplo (como en las mayora de las aplicaciones del mundo real), no se conocen los nombres de las columnas en tiempo de ejecucin; vienen de los parmetros de la solicitud. Por lo tanto, la sentencia INSERT de SQL incluye una marca de interrogacin por cada valor como un contenedor y parmetros internos de la accin que seleccionan el valor dinmicamente. Las columnas FirstName y LastName son columnas de texto y las aciones <sql:param> seleccionan sus valores al valor del parmetro de solicitud correspondiente.

Sin embargo, la columna EmpDate, es una columna de fecha, demandando una atencin especial. Primero de todo, debemos usar una variable que contenga la fecha en su forma nativa (un objeto java.util.Date) en lugar de usar el valor del parmetro de solicitud, usamos la variable crada por las acciones <fmt:parseDate> o <jsp:useBean>. Segundo, debemos usar la accin <sql:dateParam> para seleccionar el valor. En este ejemplo, hemos usado la parte de la fecha, por eso hemos seleccionado el atributo opcional type a date. Otros valores vlidos son time y timestamp (por defecto), para las columnas que slo toman la hora o la fecha y la hora.

Hay una accin JSTL ms que no hemos descrito hasta ahora: <sql:transaction>. Podemos usarla para agrupar varias acciones update (o incluso query) donde todas ellas se deben ejecutar como parte de la misma transacin de la base de datos. El ejemplo estndard es transferir una cantidad de dinero de una cuenta a otra; implementada como una sentencia SQL que elimina el dinero de la primera cuenta y otra sentencia que lo aade a la segunda.

Si encapsulamos todos los accesos a una base de datos en clases Java en lugar de usar las acciones de JSTL, todava hay una parte de JSTL que nos puede ser til. Es una clase llamada javax.servlet.jsp.jstl.sql.ResultSupport, con estos dos mtodos:

public static Result toResult(java.sql.ResultSet rs);
public static Result toResult(java.sql.ResultSet rs, int maxRows);

Podemos usar esta clase para convertir un objeto ResultSet estndar JDBC en un objeto Result JSTL antes de reenviarlo a la pgina JSP para mostrarlo. Las acciones JSTL pueden acceder fcilmente a los datos de un objeto Result, como vimos anteriormente. Otra aproximacin, todava mejor, es pasar el resultado de la consulta a la pgina JSP como una estructura de datos personalizada, como una List de beans que contienen los datos de cada fila, pero el objeto Result an es un buen candidato para prototipos y pequeas aplicaciones.

COMPARTE ESTE ARTÍCULO

ENVIAR A UN AMIGO
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN GOOGLE +
ARTÍCULO ANTERIOR

SIGUIENTE ARTÍCULO

¡SÉ EL PRIMERO EN COMENTAR!
Conéctate o Regístrate para dejar tu comentario.