lunes, 8 de abril de 2013

Un proyecto en NetBeans de principio a fin (V). Conexión con la base de datos

No es por desanimar, pero si la entrada anterior os pareció larga y cansina agarraros con ésta. Si estáis siguiendo este tutorial con tanto entusiasmo que no podéis parar de leer y enredar con NetBeans hasta llegar al final de cada post, más vale que vayáis a mear y a picar algo a la cocina... En esta ocasión vamos a centrarnos en la comunicación entre la base de datos y la aplicación. Añadiremos unos cuantos datos a la base de datos y examinaremos algunas de las características que proporciona el editor de SQL del IDE. Configuraremos el origen de datos y un pool de conexiones en el servidor GlassFish, y crearemos una página JSP para probar la conexión mediante alguna consulta simple a la base de datos.


Además veremos cómo recuperar y mostrar imágenes, cómo configurar parámetros de contexto y cómo recuperar sus valores desde las páginas web. Una vez que tengamos funcionando correctamente el origen de datos, usaremos JSTL y librerías de etiquetas SQL para recuperar y mostrar imágenes de categorías y productos.

AÑADIENDO DATOS A LA BASE DE DATOS

Podemos hacer esto desde el editor SQL del IDE, que nos permite interactuar directamente con la base de datos usando SQL nativo. El IDE incluye una interfaz gráfica con la que podemos añadir, borrar, editar y modificar registros de tablas.

Tabla "category"

En la pestaña de Servicios, pulsamos (botón derecho) sobre el icono de la tabla "category" y seleccionamos View Data. Accedemos así al editor SQL. Ejecutando el siguiente código SQL añadiremos las correspondientes tuplas para las categorías. Nótese que sólo nos preocupamos por el atributo "name", ya que el "id" es autonumérico (tal como lo definimos en el esquema):

INSERT INTO category (name) VALUES ('dairy'),('meats'),('bakery'),('fruit & veg');

Tabla "product"

A continuación ejecutaremos las siguientes instrucciones insert para introducir datos en la tabla "product":

INSERT INTO product (name, price, description, category_id) VALUES ('milk', 1.70, 'semi skimmed (1L)', 1);
INSERT INTO product (name, price, description, category_id) VALUES ('cheese', 2.39, 'mild cheddar (330g)', 1);
INSERT INTO product (name, price, description, category_id) VALUES ('butter', 1.09, 'unsalted (250g)', 1);
INSERT INTO product (name, price, description, category_id) VALUES ('free range eggs', 1.76, 'medium-sized (6 eggs)', 1);

INSERT INTO product (name, price, description, category_id) VALUES ('organic meat patties', 2.29, 'rolled in fresh herbs 2 patties (250g)', 2);
INSERT INTO product (name, price, description, category_id) VALUES ('parma ham', 3.49, 'matured, organic (70g)', 2);
INSERT INTO product (name, price, description, category_id) VALUES ('chicken leg', 2.59, 'free range (250g)', 2);
INSERT INTO product (name, price, description, category_id) VALUES ('sausages', 3.55, 'reduced fat, pork 3 sausages (350g)', 2);

INSERT INTO product (name, price, description, category_id) VALUES ('sunflower seed loaf', 1.89, '600g', 3);
INSERT INTO product (name, price, description, category_id) VALUES ('sesame seed bagel', 1.19, '4 bagels', 3);
INSERT INTO product (name, price, description, category_id) VALUES ('pumpkin seed bun', 1.15, '4 buns', 3);
INSERT INTO product (name, price, description, category_id) VALUES ('chocolate cookies', 2.39, 'contain peanuts(3 cookies)', 3);

INSERT INTO product (name, price, description, category_id) VALUES ('corn on the cob', 1.59, '2 pieces', 4);
INSERT INTO product (name, price, description, category_id) VALUES ('red currants', 2.49, '150g', 4);
INSERT INTO product (name, price, description, category_id) VALUES ('broccoli', 1.29, '500g', 4);
INSERT INTO product (name, price, description, category_id) VALUES ('seedless watermelon', 1.49, '250g', 4);

CREANDO POOL DE CONEXIONES Y ORIGEN DE DATOS

De ahora en adelante, estableceremos la conexión entre la base de datos MySQL y la aplicación affablebean a través del servidor GlassFish. Esta comunicación es posible gracias a la API JDBC (Java Database Conectivity). Aunque este tutorial no trabaja directamente con programación de JDBC, sí que usará esta API cuando sea necesaria la comunicación entre los lenguajes SQL y Java. Comenzaremos creando un pool de conexiones en el servidor GlassFish. El servidor necesita del driver de JDBC Connector/J para convertir las llamadas JDBC al protocolo específico de MySQL. Más tarde usaremos etiquetas JSTL (<sql:query>) para consultar la base de datos. Las etiquetas serán traducidas a instrucciones JDBC.

Un pool de conexiones contiene un conjunto de conexiones reutilizables para una base de datos. Puesto que crear cada nueva conexión física es muy costoso, el servidor mantiene un pool de conexiones disponibles para mejorar el rendimiento. De este modo cuando una aplicación necesita una conexión la toma del pool y al finalizar la conexión ésta retorna al pool. Los pool de conexiones emplean un driver JDBC para crear las conexiones físicas con la base de datos.

El origen de datos (data source) proporciona los medios para conectar con la base de datos. Las aplicaciones obtienen las conexiones del pool buscando un origen de datos mediante la interfaz JNDI (Java Naming and Directory Interface, una API de Java para servicios de directorio) y pidiendo después una conexión. El pool de conexiones asociado con el data source proporciona entonces la conexión con la base de datos.

Así pues, para poder acceder desde la aplicación a la base de datos, vamos a necesitar crear un pool de conexiones y un origen de datos que use dicho pool. Usaremos para ello el asistente proporcionado por el IDE (aunque también es posible hacerlo, de forma un poco más engorrosa eso sí, a través de la consola de administración de GlassFish).

A través del asistente de New File, crearemos un nuevo recurso JDBC en el servidor GlassFish (New File>GlassFish>JDBC Resource) y elegiremos crear un nuevo pool de conexiones (Create New JDBC Connection Pool) con los siguientes datos de configuración:
  • JNDI Name: jdbc/affablebean (el comienzo con "jdbc/" es una convención)
  • Object Type: user
  • Enable: trae
Continuamos y obviamos el paso de Propiedades Adicionales. A continuación ponemos "AffableBeanPool" como nombre del pool de conexiones y nos aseguramos de que la opción Extract from Existing Connection está marcada y de que la conexión jdbc:mysql://localhost:3306/affablebean está seleccionada. Continuamos...

En el siguiente paso, añadir propiedades al pool de conexiones, especificamos los siguientes detalles:
  • Datasource Classname: com.mysql.jdbc.jdbc2.optional.MysqlDataSource
  • Resource Type: javax.sql.ConnectionPoolDataSource
  • Description: Connects to the affablebean database
Nótese que, además, el asistente muestra propiedades de la conexión existente: url, usuario y password.

Cuando finalizamos el asistente, se genera el fichero "sun-resources.xml". Este fichero es un descriptor de despliegue específico del servidor de aplicaciones (GlassFish) que configurará el pool de conexiones y el origen de datos la próxima vez que sea desplegada la aplicación. Haciendo doble click en el fichero (en la ventana de Proyectos, desplegando el nodo de Recursos del Servidor) podemos abrir el fichero XML en el editor y echar un vistazo a la configuración del pool y el origen de datos.

Ahora podemos probar a desplegar la aplicación. Después podremos navegar por el árbol de recursos del servidor y comprobar que nuestro origen de datos y nuestro pool de conexiones efectivamente están presentes. A partir de aquí podemos ver y hacer cambios en sus propiedades, podemos asociar un data source con cualquier pool de conexiones registrado en el servidor, podemos editar las propiedades del pool, o incluso quitar ambos recursos. Nótese que una vez realizado el despliegue, creando el pool de conexiones y el origen de datos en el servidor, nuestro proyecto ya no necesitará más el fichero "sun-resources.xml".

PROBANDO EL POOL DE CONEXIONES Y EL ORIGEN DE DATOS

Ahora vamos a asegurarnos de que el servidor GlassFish se puede conectar correctamente con MySQL, para eso vamos a hacer un ping de toda la vida. Después toquetearemos el descriptor de despliegue web.xml para hacer una referencia al origen de datos.

Ping al pool de conexiones

Desplegando el menú contextual sobre el nodo del servidor GlassFish podremos acceder a la opción View Domain Admin Console. Esto nos abrirá una ventana de navegador, donde podremos logarnos en la consola de administración (por defecto usuario: "admin", contraseña: "adminadmin").

En el árbol de la izquierda, buscaremos nuestro pool de conexiones AffableBeanPool (Recursos>JDBC>Conjunto de Conexiones). Accederemos entonces a un formulario donde podremos editar algunas características.

La consola puede aparecer en español o en inglés (a mi me aparece en español), por lo que puede darse cierta confusión ocasional dado que yo estoy basándome en un texto en inglés. La situación se puede tornar desconcertante cuando diga que hay que pulsar el botón "ping", y lo único que nos encontremos sea un feo botón "sondeo". Personalmente me hace mucha más gracia la palabra ping (sondeo parece referirse a una aburrida encuesta del CIS...). Igualmente me gusta mucho más decir pool de conexiones que conjunto de conexiones (o piscina, que sería mucho más literal..). Soy un defensor de la lengua castellana, pero en temas informáticos tengo mis debilidades, que le vamos a hacer...

En fin, que me desvío del tema, si pulsamos sobre el botón "ping/sondeo" y nos dice que el ping se ha realizado con éxito, entonces todo guay. Pero si falla hay que ponerse revisar lo que hemos hecho, porque en algún sitio hemos metido la pata...

Crear una referencia al origen de datos

Haciendo doble click sobre web.xml (Ventana de Proyectos, carpeta de ficheros de configuración), se nos abre una interfaz en el editor del IDE. Seleccionaremos la pestaña References y añadiremos una Resource Reference en el apartado correspondiente. Introduciremos los siguientes detalles:
  • Resource Name: jdbc/affablebean
  • Resource Type: javax.sql.DataSource
  • Authentication: Container
  • Sharing Scope: Shareable
  • Description: Connects to database for AffableBean application
Podemos verificar que la referencia ha sido añadida al fichero web.xml buscando el siguiente código XML:

<resource-ref>
    <description>Connects to database for AffableBean application</description>
    <res-ref-name>jdbc/affablebean</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
    <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

Consultar a la base de datos desde una página JSP

Usando el ya de sobra conocido wizard vamos a crear una nueva página JSP para probar el origen de datos. La llamaremos "testDataSource" y la colocaremos en una nueva carpeta que llamaremos "test".

En la ventana del editor, dentro del body de la página, pulsaremos Ctrl+Space para ver las sugerencias de código. Elegiremos DB Report de la lista de opciones. Esto nos abrirá un cuadro de dialogo donde podremos especificar el origen de datos y hacer una consulta SQL. Introduciremos nuestro data source: jdbc/affablebean y el siguiente código SQL:

SELECT *
FROM category, product
WHERE category.id = product.category_id

Al pulsar "OK" se generan las referencias a las librerías JSTL y SQL en la parte superior del código, así como una plantilla que mostrará el resultado de la consulta en una tabla HTML. El código de nuestra página de pruebas queda como sigue:

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

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>JSP Page</title>
 </head>
 <body>
  <h1>Hello World!</h1>
  <sql:query var="result" dataSource="jdbc/affablebean">
   SELECT *
   FROM category, product
   WHERE category.id = product.category_id
  </sql:query>
    
  <table border="1">
  <!-- column headers -->
  <tr>
   <c:forEach var="columnName" items="${result.columnNames}">
    <th><c:out value="${columnName}"/></th>
   </c:forEach>
  </tr>
  <!-- column data -->
  <c:forEach var="row" items="${result.rowsByIndex}">
  <tr>
   <c:forEach var="column" items="${row}">
    <td><c:out v alue="${column}"/></td>
   </c:forEach>
  </tr>
  </c:forEach>
  </table>
 </body>
</html>

Antes de ejecutar el archivo en el navegador, debemos asegurarnos de que está activa la opción de despliegue del driver JDBC. Si accedemos a Tools>Servers, podremos ver -seleccionando el servidor GlassFish- si la opción "Enable JDBC Driver Deployment" está activada. Para que una aplicación Java se conecte a una base de datos, el servidor necesita del driver JDBC para ser capaz de tender un puente de comunicación entre los lenguajes SQL y Java. En el caso de MySQL se usa el controlador JDBC Connector/J (del que ya hemos hablado con anterioridad). Tener habilitada la opción mencionada, nos asegura que el servidor comprobará si necesita el driver, en cuyo caso será desplegado por el IDE. De otro modo, tendríamos que colocar a mano el archivo JAR del controlador en el directorio lib del servidor.

Si ahora ejecutamos la página en el browser, veremos una bonita tabla con los datos de la consulta ejecutada. Con esto, podemos afirmar, henchidos de orgullo, que hemos configurado un origen de datos y un pool de conexiones en el servidor. Y queda demostrado que la aplicación puede acceder a los datos de la base de datos affablebean.

CONFIGURANDO PARÁMETROS DE CONTEXTO

El propietario de una aplicación puede querer tener la posibilidad de cambiar ciertas configuraciones sin necesidad de hacer cambios intrusivos en el código fuente. Esto es lo que permiten hacer los parámetros de contexto, habilitan el acceso a ciertos aspectos de la aplicación mediante parámetros, y proporcionan, de ser necesario, un modo cambiar el valor de esos parámetros desde una localización sencilla.

La configuración de los parámetros de contexto puede ser llevada a cabo mediante dos pasos:
  1. Listar los nombres de los parámetros y sus valores en el descriptor de despliegue.
  2. Llamar a los parámetros en páginas JSP mediante el objeto initParam.
El Lenguaje de Expresiones de JSP (EL por sus siglas en inglés) es un lenguaje utilizado en páginas JSP para interactuar con los datos entregados por el servidor, que combinado con la librería JSTL Core nos permite construir la lógica de las páginas de forma amena y eficaz. JSP EL define objetos implícitos, de los cuales initParam es un ejemplo. Así, cuando trabajamos con páginas JSP, podemos hacer uso de estos objetos implícitos utilizando dot notation y colocar delimitadores EL (${…}). Por ejemplo, si queremos inicializar un parámetro llamado "myParam", podemos accederlo desde una página JSP con la expresión ${initParam.myParam}.

Para ver un ejemplo, vamos a crear parámetros de contexto para los paths de las imágenes de categorías y productos usados en el proyecto. Así que lo primero que haremos será descargar las imágenes de este enlace. El archivo comprimido contiene una carpeta img que debemos copiar en el proyecto, preferiblemente dentro de la carpeta Web Pages. Esta carpeta contiene todas las imágenes que necesitaremos a lo largo de este tutorial.

Ahora podemos volver a abrir en el editor a nuestro amigo web.xml. En la pestaña general vamos a expandir la opción Context Parameters y vamos a añadir un par de filas con los siguientes detalles:
  • Parameter Name: productImagePath
  • Parameter Value: img/products/
  • Description: The relative path to products images
Y después:
  • Parameter Name: categoryImagePath
  • Parameter Value: img/categories/
  • Description: The relative path to categories images
Si examinamos ahora el código XML, veremos que se han agregado sendas entradas <context-param>. Para comprobar que los valores para los parámetros de contexto están accesibles desde las páginas web, podemos abrir cualquiera de las páginas en el editor y añadir expresiones EL que usen el objeto implícito initParam. Por ejemplo en la página index.jsp podemos introducir el siguiente código (dentro del <div> con el texto de bienvenida por ejemplo):

<!-- test to access context parameters -->
categoryImagePath: ${initParam.categoryImagePath}
productImagePath: ${initParam.productImagePath}

Si ejecutamos el proyecto, veremos como en la página index aparece el valor de los parámetros inicializados por el objeto initParam.

TRABAJANDO CON JSTL

Ahora que ya sabemos cómo acceder a los datos, hemos añadido recursos de imágenes al proyecto y hemos configurado varios parámetros de contexto, vamos a combinar tan magníficos conocimientos adquiridos para colocar imágenes en la aplicación. Y para ello vamos a empezar a sacarle partido a la librería JSTL (JavaServer Standard Tag Library).

Nótese que no nos tenemos que preocupar por añadir la librería (jstl-impl.jar) a mano, porque ésta ya se añadió en el classpath del proyecto cuando seleccionamos GlassFish como nuestro servidor de desarrollo.

Empecemos a poner esto bonito...

Página index

En la página index, al principio del todo, justo antes del primer <div>, escribimos "db" y pulsamos Ctrl+Space para recibir sugerencias de código. Seleccionamos "DB Query". En el cuadro de diálogo introducimos los siguientes valores:
  • Variable number: categories
  • Scope: page
  • Data Source: jdbc/affablebean
  • Query Statement: SELECT * FROM categories
Esto generará una consulta SQL usando etiquetas JSTL, así como una referencia a la librería de tags de SQL (en la parte superior del código de la página).

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

<sql:query var="categories" dataSource="jdbc/affablebean">
    SELECT * FROM category
</sql:query>

[...]

Los datos devueltos por la consulta se almacenan como un conjunto de resultados en la variable "categories" y pueden se accedidos mediante sintaxis EL (por ejemplo: ${categories})

Ahora nos posicionaremos al principio del bloque <div id=”indexRightColumn">, teclearemos "jstl" y pulsaremos Ctrl+Space para ver las sugerencias de código. Seleccionamos "JSTL For Each" y en cuadro de diálogo pondremos:
  • Collection: ${categories.rows}
  • Current Item of the Iteration: category
De este modo implementaremos un bucle For Each usando etiquetas JSTL (obsérvese que también se incluye una nueva referencia a la librería de etiquetas core).

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

    ...

    <div id="indexRightColumn">
        <c:forEach var="category" items="categories.rows">
        </c:forEach>
        <div class="categoryBox">

Claro, aquí podemos preguntarnos que demonios significa "rows" en el código generado. Pues bien, como ya he dicho, la variable "categories" representa a un conjunto de resultados; más específicamente se refiere a un objeto que implementa la interfaz javax.servlet.jsp.jstl.sql.Result. Este objeto permite acceder a filas, nombres de columnas y tamaños del conjunto de resultados de la consulta. Cuando usamos notación de puntos como en el caso anterior, "categories.rows", esto es traducido a Java como "categories.getRows()".

Ahora, gracias a la integración en la página de las etiquetas <c:forEach>, podemos anidar las etiquetas <div class=categoryBox> dentro del bucle For Each de forma que se generen etiquetas HTML para cada una de las cuatro categorías. Usando sintaxis EL podemos extraer de la tabla "category" los valores mediante los identificadores y nombres de las columnas. (no debemos olvidar borrar los anteriores tags <div class=categoryBox>). Así, el bloque <div id="indexRightColumn"> quedará como sigue:

<div id="indexRightColumn">
 <c:forEach var="category" items="${categories.rows}">
  <div class="categoryBox">
   <a href="category?${category.id}">
    <span class="categoryLabelText">${categoyName}</span>
    <img src="${initParam.categoryImagePath}${category.name.jpg}" alt="${category.name}">
   </a>
  </div>
 </c:forEach>
</div>

Y ahora podríamos ver el resultado ejecutando el proyecto en el navegador y asombrarnos de nuestro brillante intelecto...

Ya hemos visto lo chulas que quedan nuestras primeras imágenes en la web, pero vamos a pararnos un poco en entender cómo funciona el enlace entre la página index y la página category. Reexaminemos la siguiente etiqueta que tenemos dentro del bucle:

<a href="category?${category.id}">

Cuando hacemos click en una imagen con enlace, se envía una petición para "category" al root del contexto de la aplicación en el servidor. En nuestro entorno de desarrollo la URL queda así:

http://localhost:8080/AffableBean/category

Es decir, tenemos la ubicación por defecto del servidor GlassFish (localhost y puerto), tenemos el root de contexto donde hemos desplegado la aplicación y tenemos el path para la petición (recordemos que "category" no es más que un mapeo de la vista "category.jsp" en el Servlet del Controlador)

Además encadenamos a la URL solicitada el signo de interrogación (?) y el ID de la categoría, que nos permite determinar qué detalles de category necesitamos incluir en la respuesta. Todo esto forma la cadena de consulta.

Página category

Hay tres aspectos de la página de categorías que deben ser manejados dinámicamente: 1) La columna izquierda debe mostrar que categoría está seleccionada. 2) En la cabecera de la tabla debe mostrarse el nombre de la categoría seleccionada. 3) El contenido de la tabla debe listar los detalles pertinentes de la categoría seleccionada. Para los tres casos debemos actuar en base a dos sencillos pasos:
  1. Recuperar datos de la base de datos usando la librería de tags sql proporcionada por JSTL
  2. Mostrar los datos usando la librería core facilitada por JSTL y sintaxis EL
Veamos cada tarea por separado.

Mostrar la categoría seleccionada en la columna izquierda

En la parte superior del código de la página "category.jsp" introducimos la siguiente consulta:

<sql:query var="categories" dataSource="jdbc/affablebean">
    SELECT * FROM category
</sql:query>

Y entre las etiquetas <div id="categoryLeftColumn"> reemplazamos el contenido estático por el siguiente bucle For Each:

<div id="categoryLeftColumn">
   <c:forEach var="category" items="${categories.rows}">
      <c:choose>
         <c:when test="${category.id == pageContext.request.queryString}">
            <div class="categoryButton" id="selectedCategory">
               <span class="categoryText">
                  ${category.name}
               </span>
            </div>
         </c:when>
         <c:otherwise>
            <a href="category?${category.id}" class="categoryButton">
               <div class="categoryText">
                  ${category.name}
               </div>
            </a>
         </c:otherwise>
      </c:choose>
   </c:forEach>
</div>

Para estos dos últimos pasos podemos utilizar los cuadros de diálogo como anteriormente, o usar las sugerencias de código del editor mientras escribimos. En cualquier caso debemos comprobar que se han agregado al principio de la página las directivas JSTL core y sql taglib, como en la página index.

En el fragmento de código anterior, se puede ver que accedemos a la cadena de consulta de la petición mediante "pageContext.request.queryString"; pageContext es otro objeto implícito definido por el Lenguaje de Expresiones de JSP. La expresión EL usa el objeto pageContext para acceder a la actual petición (que es un objeto HTTPServletRequest). Luego, el método getQueryString() es llamado desde HTTPServletRequest para obtener el valor de la cadena de consulta.

Ahora, si ejecutamos el proyecto y navegamos por la página de categorías, podremos ver que con cada click se destaca la categoría seleccionada.

El contenedor de servlets (en nuestro caso GlassFish) convierte las páginas JSP en servlets antes de ejecutarlos como parte de un proyecto. Por si vuestra ansia de conocimientos no tiene límites, tal vez queráis saber que es posible ver los servlets generados accediendo a la opción view servlets en el menú contextual disponible cuando pinchamos con el botón derecho en el nodo de la página (dentro de la ventana de Proyectos). Obviamente hay que ejecutar el proyecto para que se generen los servlets. Así podremos ver en el IDE una copia -sólo lectura- de los servlets generados en el servidor.

Mostrar el título de cabecera sobre la tabla de productos

Vamos a incluir una nueva consulta en la página de categorías, justo a continuación de la implementada en el apartado anterior:

<sql:query var="selectedCategory" dataSource="jdbc/affablebean">
        SELECT name FROM category WHERE id = ?
        <sql:param value="${pageContext.request.queryString}" />
</sql:query>

Ahora, usando sintaxis JSP EL, extraemos el nombre de la categoría de la consulta y la mostramos en la página. El elemento <p id=”categoryTitle”> queda como sigue:

<p id=”categoryTitle”>${selectedCategory.rows[0].name}</p>

Puesto que el resultado de la consulta siempre va a ser un único ítem (sólo podemos seleccionar una categoría), podemos recuperar la primera -y única- fila del resultado con selectedCategory.rows[0], así como cada una de sus columnas -name en este caso- sin mayores problemas.

Mostrar detalles de los productos en la tabla

Volvemos a la página de categorías para añadir una nueva consulta:

<sql:query var="categoryProducts" dataSource="jdbc/affablebean">
        SELECT * FROM product WHERE category_id = ?
        <sql:param value="${pageContext.request.queryString}" />
</sql:query>

Ahora, dentro de las etiquetas del bloque de la tabla "productTable", sustituimos las filas estáticas por el siguiente bucle forEach:

<table id="productTable">
   <c:forEach var="product" items="${categoryProducts.rows}" varStatus="iter">
      <tr class="${((iter.index % 2) == 0) ? 'lightBlue' : 'white'}">
         <td>
            <img src="${initParam.productImagePath}${product.name}.png"
                    alt="${product.name}">
         </td>
         <td>
            ${product.name}
            <br />
            <span class="smallText">${product.description}</span>
         </td>
         <td>
            &euro; ${product.price} / unit
         </td>
         <td>
            <form action="addToCart" method="post">
               <input type="hidden"
                      name="productId"
                      value="${product.id}">
               <input type="submit"
                      value="add to cart">
            </form>
         </td>
      </tr>
   </c:forEach>
</table>

Ahora vamos a examinar un fragmento en concreto. Podemos ver que en código anterior existe una expresión EL para determinar el color de fondo de las filas de la tabla: "class="${((iter.index % 2) == 0) ? 'lightBlue' : 'white'}"".

El atributo "varStatus" es un objeto que implementa la interfaz LoopTagStatus. Así, "iter.index" recupera el índice de la iteración actual del bucle, y devuelve un valor lógico (boolean) basado en el resultado. Lo que hacemos en esa expresión es, en definitiva, evaluar el resto de una división ((iter.index % 2) == 0), y luego con el operador condicional EL (? :) devolvemos un color u otro en función del resultado.

Ahora podemos salvar los cambios y ver que tal ha quedado en el browser. Y con esto -que no es poco- habríamos terminado con esta unidad. Ya sabemos conectar la aplicación a la base de datos a través de un pool de conexiones y un origen de datos en el servidor. Hemos creado algunos parámetros de contexto y sabemos acceder a ellos desde páginas JSP. Hemos implementado etiquetas JSTL en las páginas de la aplicación para recuperar y mostrar dinámicamente datos de la base de datos... En fin todo un mundo de posibilidades.

La próxima vez que nos veamos, en la emocionante sexta entrega, hablaremos de clases de entidades persistentes y beans de sesión.

2 comentarios:

  1. Muchas gracias Luís Adolfo. Aún quedan unas cuantas entregas que compartiré tan pronto como tenga tiempo. Mientras tanto, si no te da pereza el inglés, puedes consultar el tutorial en su fuente original de "netbeans.org".

    ResponderEliminar