La aplicaci�n Web utilizada para demostrar las tecnolog�as usadas en este tutorial es una simple aplicaci�n de control de una librer�a (solo control y ubicaci�n de los libros, no hay facturaci�n, por ejemplo), llamada Libreria . No es el prop�sito de este tutorial crear una aplicaci�n completa, es s�lo un ejemplo.
Libreria tiene tres clases Java:
- libreria.ContextListener es un oyente del contexto servlet al que Tomcat llamar� cuando inicie y detenga la aplicaci�n Libreria. Esta clase crea un objeto de la clase LibrosBD y lo almacena como un atributo de contexto con el nombre BaseDatos
- libreria.LibrosBD es una clase que representa la base de datos de libros. Su constructor establece una conexi�n a la base de datos (utilizando el objeto LibrosBD que se obtiene del atributo de contexto BaseDatos), y la sesi�n se puede compartir entre varias sesiones web. La clase proporciona varios m�todos de acceso a la base de datos (para insertar, borrar, modificar u obtener registros de la base de datos).
- libreria.Libro es una clase Java que representa un libro. Las p�ginas JSP de la aplicaci�n utilizan esta clase como un Bean contenedor de datos para mostrarlos.
�libreria.ContextListener
libreria.ContextListener es un oyente del contexto servlet al que se llamar� cuando arranque y cuando se pare la aplicaci�n Libreria. Esto se configura en el descriptor de despliegue de la aplicaci�n web.xml. Cuando se arranca la aplicaci�n Libreria se crea un ejemplar de la clase LibrosBD y se almacena como un atributo de contexto; cuando se cierra la aplicaci�n, el objeto LibrosBD se recupera del atributo de contexto y se cierra la conexi�n con la base de datos. Entre tanto, las JSP recuperan el objeto LibrosBD del atributo de contexto siempre que necesitan acceder a la base de datos.
�Prop�sito de la clase ContextListener
El prop�sito de la clase Java libreria.ContextListener es crear un ejemplar de libreria.LibrosBD y almacenarlo como un atributo del contexto servlet identificado como: "BaseDatos". Esto permitir� a las p�ginas JSP recuperar el ejemplar de libreria.LibrosBD y acceder a la tabla Libros.
libreria.ContextListener est� configurado como un oyente de contexto servlet en el fichero web.xml.
�M�todos de la clase ContextListener
-
contextInitialized
Se llama a contextInitialized siempre que la aplicaci�n web arranca.
Primero se recupera el contexto servlet desde el evento de contexto servlet:
ServletContext servletContext = servletContextEvent.getServletContext ();
Luego se crea un ejemplar de LibrosBD y se graba en el contexto servlet identificado como: "BaseDatos". Si este proceso lanza una excepci�n se guarda un log:
try { LibrosBD LibrosBD = new LibrosBD (); servletContext.setAttribute ("BaseDatos", LibrosBD); } catch (Exception e) { servletContext.log ("No se pudo crear el atributo BaseDatos: " + e.getMessage()); }
-
contextDestroyed
Se llama a contextDestroyed cuando se cierra la aplicaci�n web.
Primero se recupera el contexto servlet desde el evento ServletContexto:
ServletContext servletContext = servletContextEvent.getServletContext ();
Luego se recupera el objeto LibrosBD desde el contexto y se cierra la base de datos:
LibrosBD LibrosBD = (LibrosBD) servletContext.getAttribute ("BaseDatos"); LibrosBD.close ();
Luego se elimina el atributo del contexto servlet:
servletContext.removeAttribute ("BaseDatos");
�C�digo Fuente Completo de la clase ContextListener
// // ContextListener.java // package libreria; import javax.servlet.*; public final class ContextListener implements ServletContextListener { public void contextInitialized (ServletContextEvent servletContextEvent) { ServletContext servletContext = servletContextEvent.getServletContext (); try { LibrosBD LibrosBD = new LibrosBD (); servletContext.setAttribute ("BaseDatos", LibrosBD); } catch (Exception e) { servletContext.log ("No se pudo crear el atributo BaseDatos: " + e.getMessage ()); } } public void contextDestroyed (ServletContextEvent servletContextEvent) { ServletContext servletContext = servletContextEvent.getServletContext (); LibrosBD LibrosBD = (LibrosBD) servletContext.getAttribute ("BaseDatos"); LibrosBD.close (); servletContext.removeAttribute ("BaseDatos"); } }
�libreria.LibrosBD
libreria.LibrosBD es una clase que representa la base de datos de libros. Su constructor establece una conexi�n a la base de datos, y la sesi�n se puede compartir entre varias sesiones web. La clase proporciona varios m�todos de acceso a la base de datos.
�Prop�sito de la clase LibrosBD
El prop�sito de la clase libreria.LibrosBD es establecer una conexi�n a la base de datos p�blica y proporcionar m�todos para acceder de forma segura y actualizar la tabla Libros contenida en la base de datos sin conflictos con otros usuarios que est�n accediendo a los mismos datos.
S�lo hay una conexi�n a la base de datos que es compartida por todos los usuarios de la aplicaci�n Web Libreria. Esto es adecuado mientras el n�mero de usuarios concurrentes no sea excesivo. Vamos a mantenerlo lo m�s simple posible, s�lo debemos a�adir soporte para varias conexiones si es realmente necesario. Observa, sin embargo, que si vamos a realizar esas complejidades, es en esta clase Java donde debemos hacerlas.
�M�todos de la Clase LibrosBD
- Constructor de LibrosBD:
Establece una conexi�n a la base de datos p�blica.
Hasta hace poco, si quer�amos establecer una conexi�n a una base de datos desde un Servlet bajo Tomcat ten�amos que codificar el driver JDBC, la URL de la base de datos, y el nombre de usuario y su password dentro del Servlet de esta forma:
Class.forName ("org.gjt.mm.mysql.Driver").newInstance (); connection = DriverManager.getConnection ("jdbc:mysql://localhost/MiBase", "mysqlusername", "mysqlpassword");
No es buena idea codificar dicha informaci�n en el c�digo fuente de la aplicaci�n. Tomcat 4 resuelve este problema permitiendo al Servlet acceder a Tomcat y recuperar una conexi�n desde el DataSource que se define en el fichero Libreria.xml, como veremos m�s adelante.
Primero se recupera el javax.naming.InitialContext para que se pueda realizar una b�squeda JNDI:
InitialContext initialContext = new InitialContext ();
Despu�s se recupera el contexto de la aplicaci�n Libreria:
Context envContext = (Context) initialContext.lookup ("java:comp/env");
Luego se recupera el propio DataSource:
DataSource dataSource = (DataSource) envContext.lookup ("jdbc/biblioteca");
Y por �ltimo se recupera la conexi�n desde el DataSource:
this.connection = dataSource.getConnection ();
Todos los par�metros del DataSource tenemos que definirlos en el fichero Libreria.xml (que veremos m�s adelante), aunque tambi�n se pueden modificar usando las facilidades de administraci�n de Tomcat, con la direcci�n http://localhost:8080/admin .
-
getConnection
Se utilizan los m�todos getConnection y releaseConnection para asegurarse de que la conexi�n a la base de datos se puede compartir de forma segura, el recurso de la conexi�n se controla utilizando los m�todos sincronizados getConnection y releaseConnection.
Primero getConnection espera hasta que se libera la conexi�n:
while (this.connectionFree == false) { try { wait (); } catch (InterruptedException e) { } }
Luego marca el indicador de conexi�n libre como no libre, as� bloquea la conexi�n hasta que se llame a releaseConnection:
this.connectionFree = false;
Luego se lo notifica a cualquier otro thread de LibrosBD que est� esperando una conexi�n:
notify ();
Finalmente devuelve la conexi�n:
return this.connection;
-
releaseConnection
Primero espera hasta que la conexi�n est� ocupada:
while (this.connectionFree == true) { try { wait (); } catch (InterruptedException e) { } }
Luego selecciona el indicador de conexi�n libre a libre, as� desbloquea la conexi�n hasta que se llame a getConnection:
this.connectionFree = true;
Finalmente se lo notifica a cualquier otro thread de LibrosBD que est� esperando una conexi�n:
notify ();
Todas las dem�s rutinas que acceden a la base de datos emplean getConnection y releaseConnection, para que s�lo pueda acceder a la base de datos un thread a la vez.
-
getLibro
El m�todo getLibro toma un par�metro id y devuelve el Libro para ese id; si no se puede encontrar ese id se devuelve un Libro null.
getLibro primero obtiene una conexi�n libre
this.getConnection ();
Luego crea una sentencia select, e inserta el id en la posici�n adecuada:
PreparedStatement preparedStatement = this.connection.prepareStatement ("SELECT * FROM Libros WHERE id = ?"); preparedStatement.setString (1, id);
Luego ejecuta la sentencia:
ResultSet resultSet = preparedStatement.executeQuery ();
Si la sentencia select devuelve datos, se ensambla un objeto Libro con los datos, se cierra la consulta sql; se libera la conexi�n se devuelve el objeto Libro:
if (resultSet.next ()) { Libro libro = new Libro ( resultSet.getString (1), resultSet.getString (2), resultSet.getString (3), resultSet.getString (4), resultSet.getString (5), resultSet.getString (6), resultSet.getString (7) ); preparedStatement.close (); this.releaseConnection (); return libro; }
Si la sentencia select no devuelve datos, se cierra la sentencia, se libera la conexi�n, y se devuelve un objeto Libro null:
else { preparedStatement.close (); this.releaseConnection (); return null; }
-
insertarLibro, borrarLibro, modificarLibro, y obtenerLibros
Todos estos m�todos utilizan una t�cnica similar a la del m�todo getLibro
-
close
close simplemente cierra la conexi�n:
this.connection.close ();
�C�digo Fuente Completo de la clase LibrosBD
// // LibrosBD.java // package libreria; import java.sql.*; import java.util.*; import javax.sql.*; import javax.naming.*; public class LibrosBD { Connection connection; private boolean connectionFree = true; private ArrayList libros; public LibrosBD () throws Exception { try { InitialContext initialContext = new InitialContext (); Context envContext = (Context) initialContext.lookup ("java:comp/env"); DataSource dataSource = (DataSource) envContext.lookup ("jdbc/biblioteca"); this.connection = dataSource.getConnection (); } catch (Exception e) { throw new Exception ("No se pudo abrir la base de datos biblioteca: " + e.getMessage ()); } } protected synchronized Connection getConnection () { while (this.connectionFree == false) { try { wait (); } catch (InterruptedException e) { } } this.connectionFree = false; notify (); return this.connection; } protected synchronized void releaseConnection () { while (this.connectionFree == true) { try { wait (); } catch (InterruptedException e) { } } this.connectionFree = true; notify (); } public Libro getLibro (String id) { try { this.getConnection (); PreparedStatement preparedStatement = this.connection.prepareStatement ("SELECT id, isbn, editorial, autor, categoria, titulo, ubicacion FROM libros" + " WHERE id = ?"); preparedStatement.setString (1, id); ResultSet resultSet = preparedStatement.executeQuery (); if (resultSet.next ()) { Libro libro = new Libro ( resultSet.getString (1), resultSet.getString (2), resultSet.getString (3), resultSet.getString (4), resultSet.getString (5), resultSet.getString (6), resultSet.getString (7) ); preparedStatement.close (); this.releaseConnection (); return libro; } else { preparedStatement.close (); this.releaseConnection (); return null; } } catch (SQLException e) { this.releaseConnection (); return null; } } public int insertarLibro (Libro libro) { int rowsAffected = 0; try { this.getConnection (); PreparedStatement preparedStatement = this.connection.prepareStatement ("INSERT INTO libros (isbn, editorial, autor, categoria, titulo, " + " ubicacion) VALUES (?, ?, ?, ?, ?, ?)"); preparedStatement.setString (1, libro.getIsbn ()); preparedStatement.setString (2, libro.getEditorial ()); preparedStatement.setString (3, libro.getAutor ()); preparedStatement.setString (4, libro.getCategoria ()); preparedStatement.setString (5, libro.getTitulo ()); preparedStatement.setString (6, libro.getUbicacion ()); rowsAffected = preparedStatement.executeUpdate (); preparedStatement.close (); this.releaseConnection (); } catch (SQLException e) { this.releaseConnection (); return 0; } return rowsAffected; } public int borrarLibro (String id){ int rowsAffected = 0; try { this.getConnection (); PreparedStatement preparedStatement = this.connection.prepareStatement ("DELETE FROM libros WHERE id = ?"); preparedStatement.setString (1, id); rowsAffected = preparedStatement.executeUpdate (); preparedStatement.close (); this.releaseConnection (); } catch (SQLException e) { this.releaseConnection (); return 0; } return rowsAffected; } public int modificarLibro (Libro libro) { int rowsAffected = 0; try { this.getConnection (); PreparedStatement preparedStatement = this.connection.prepareStatement ("UPDATE libros SET isbn=?, editorial=?," + " autor=?, categoria=?, titulo=?, ubicacion=? WHERE id =?"); preparedStatement.setString (1, libro.getIsbn ()); preparedStatement.setString (2, libro.getEditorial ()); preparedStatement.setString (3, libro.getAutor ()); preparedStatement.setString (4, libro.getCategoria ()); preparedStatement.setString (5, libro.getTitulo ()); preparedStatement.setString (6, libro.getUbicacion ()); preparedStatement.setString (7, libro.getId ()); rowsAffected = preparedStatement.executeUpdate (); preparedStatement.close (); this.releaseConnection (); } catch (SQLException e) { this.releaseConnection (); return 0; } return rowsAffected; } public Collection getLibros () { libros = new ArrayList (); try { this.getConnection (); PreparedStatement preparedStatement = this.connection.prepareStatement ("SELECT id, isbn, editorial, autor, categoria, titulo, ubicacion FROM libros"); ResultSet resultSet = preparedStatement.executeQuery (); while (resultSet.next ()) { Libro libro = new Libro ( resultSet.getString (1), resultSet.getString (2), resultSet.getString (3), resultSet.getString (4), resultSet.getString (5), resultSet.getString (6), resultSet.getString (7) ); libros.add (libro); } preparedStatement.close (); } catch (SQLException e) { return null; } this.releaseConnection (); return libros; } public void close () { try { this.connection.close (); } catch (SQLException e) { System.out.println (e.getMessage ()); } } }
�libreria.Libro
libreria.Libro es una clase Java que representa un libro. Las clases que recolectan datos todav�a son una buena idea cuando se programan JSPs, por eso esta clase es una buena idea para la aplicaci�n Libreria.
El constructor de libreria.Libro almacena los campos del libro en el objeto. Esta clase tambi�n incluye los habituales m�todos get para recuperar los datos del libro:
-
Libro(String id, String isbn, String editorial, String autor, String categoria,
String titulo, String ubicacion) - getId()
- getIsbn()
- getEditorial()
- getAutor()
- getCategoria()
- getTitulo()
- getUbicacion()
�Prop�sito de la clase Libros
El prop�sito de esta clase es recolectar los datos relativos a un libro de nuestra base de datos.
�M�todos de la clase Libros
- Constructor de la clase Libro
El constructor almacena los campos suministrados del libro:
this.id = id; this.isbn = isbn; this.editorial = editorial; this.autor = autor; this.categoria = categoria; this.titulo = titulo; this.ubicacion = ubicacion;
-
getId, getIsbn, getEditorial, getAutor, getCategoria, getTitulo y getUbicacion
Estos m�todos devuelven los distintos campos que componente un libro.
�C�digo Fuente Completo de la clase Libro
// // Libro.java // package libreria; public class Libro { private String id = null; private String isbn = null; private String editorial = null; private String autor = null; private String categoria = null; private String titulo = null; private String ubicacion = null; public Libro (String id, String isbn, String editorial, String autor, String categoria, String titulo, String ubicacion) { this.id = id; this.isbn = isbn; this.editorial = editorial; this.autor = autor; this.categoria = categoria; this.titulo = titulo; this.ubicacion = ubicacion; } public String getId () { return this.id; } public String getIsbn () { return this.isbn; } public String getEditorial () { return this.editorial; } public String getAutor () { return this.autor; } public String getCategoria () { return this.categoria; } public String getTitulo () { return this.titulo; } public String getUbicacion () { return this.ubicacion; } }