May 08 2009

Conectarse a MySQL en Tomcat

Categoría: DAD,Informática,TecnologíaMiguel Angel @ 16:37

Existen varias opciones a la hora de conectarse desde una aplicación Tomcat a una base de datos .

Nosotros lo haremos contra una base de datos MySQL. Y para ello necesitaremos el driver que descargaremos de la web de MySQL. Sólo hay que copiar el fichero mysql-connector-java*.jar en el directorio WEB-INF/lib/.

Veamos dos formas diferentes de realizar una conexión. Aunque nos quedaremos con la segunda siempre es bueno conocer otras opciones menos válidas y saber los motivos.

Opción 1: En el servlet Control

  1. Creamos un atributo para la conexión
    • private Connection Conexion = null;
  2. Creamos un método estático getConexion que devuelva una conexión con la base de datos, creándola si no existe.
  3. Sobreescribimos el método destroy para asegurarnos que se cierra la conexión con la base de datos una vez que se destruye el servlet.

Esta opción tiene algunos inconvenientes:

  • La conexión puede quedarse abierta hasta que se destruye el servlet. La solución sería cerrarla cada vez que terminamos de hacer una transacción pero esto conlleva abrir y cerrar varias veces en una misma petición si se realizan varias consultas.
  • No tenemos control sobre la conexión si se produce una excepción.
  • Son las clases que hacen uso de la conexión las encargadas de abrir y cerrar. Esto no es del todo correcto.
  • Y el más importante. Estamos utilizando un atributo y un método estáticos y, por tanto, la conexión es compartida por peticiones.

Opción 2: Utilizando un hilo local

Si queremos solucionar el último inconveniente debemos utilizar hilos locales. ¿Como se hace eso? Muy fácil. (Esta página lo explica).

Como en otras ocasiones, os dejo el fichero de la clase (ucam.servlets.Singleton) que hace uso de esta funcionalidad y hago unos comentarios sobre el código.

Lo primero que cabe destacar es que dentro de la propia clase Singleton se ha declarado otra,  ThreadLocalConnection, que hereda de ThreadLocal. ¿Que se consigue con esto? Encapsular un Object (o cualquier subclase de esta) y devolver una instancia distinta dependiendo del ObjectId del objeto que la solicita. Es decir, si la clase ThreadLocalConnection guarda un objeto de la clase Connection, dependiendo del objeto que invoque el método get de la primera, esta devolverá una instancia u otra de la segunda. Y, por tanto, dos conexiones diferentes.

Esto es válido para Tomcat ya que el objeto HttpServletRequest que representa la petición es distinto para cada una de ellas. Así, al hacer get sobre ThreadLocalConnection obtendremos un objeto conexión diferente para cada petición.

Bueno… eso no es del todo cierto ya que Tomcat reutiliza los objetos que representan las peticiones. Es decir, el servidor tiene varias instancias de la clase RequestFacade (que encapsula el objeto HttpServletRequest implementación de ServletRequest) y cuando le llega una petición coge una y la pasa como parámetro al servlet. Nuestro esquema sigue funcionando ya que a Tomcat nunca se le ocurre utilizar el mismo objeto petición para dos peticiones concurrentes. Por lo que al hacer Singleton.getConexion() lo estamos haciendo desde objetos diferentes y ThreadLocal devolverá conexiones distintas.

Es importante utilizar un filtro y así hacer commit o rollback al terminar la petición. Como se reutiliza el objeto petición y, por tanto el objeto conexión, todas las operaciones de peticiones anteriores habrán sido confirmadas o eliminadas. Esto lo mejoraremos más adelante utilizando un pool de conexiones. Este es el código para la clase ucam.filtros.FiltroConexion. Como se puede observar se cierra la conexión si esta fue abierta haciendo previamente commit o rollback.

El segundo punto a destacar es que se ha creado un atributo de la clase ThreadLocalConnection que será utilizado en el método getConexion para recuperar el objeto Connection que encapsula.

El método getConexion devuelve una conexión a la base de datos. Primero comprueba que existe una instancia de ThreadLocalConnection y si no la crea. Una vez que ya disponemos de la instancia lo que hacemos es solicitar, mediante el método get, el objeto de Connection. Que, como ya hemos comentado, será diferente para cada objeto petición. Por ejemplo, al hacer Singleton.getConexion() en un modelo que se está ejecutando desde una petición con id 45 el ThreadLocalConnection devolverá una instancia de Connection. Mientras que para un modelo sobre la instancia con id 12 devolverá otra.

Un punto importante dentro de getConexion es que se comprueba que la conexión no esté cerrada o sea null. Es decir, peticiones anteriores la cerraron. En cuyo caso se guarda la conexión mediante el método set. Al cual se le pasa como parámetro la instancia que devuelve el método initialValue. Este es invocado cada vez que se inicia una nueva instancia del objeto contenido en ThreadLocal.

Utilizar un pool de conexiones

Junto con las dos opciones anteriores se puede utilizar un sistema que gestione las conexiones en vez de crear una nueva cada vez. De tal forma que nuestra aplicación vaya a él, pida una conexión y, si hay alguna disponible, se la sirva.

Con esto se mejora el rendimiento ya que la conexión está disponible y no hay que estar abriendo y cerrándola. Es importante destacar que cuando desde la aplicación invocamos el método close realmente no se cierra si no que vuelve al pool y pasa a estar disponible para otra petición.

¿Como lo vamos a hacer? En Tomcat se utiliza un recurso de tipo fuente de datos (DataSource).

Crear un recurso

El código necesario para declarar un recurso es:

<Resource name=”jdbc/miServidor auth=”Container type=”javax.sql.DataSource driverClassName=”org.gjt.mm.mysql.Driver url=”jdbc:mysql://localhost:3306/sia username=” password=” maxActive=”20 maxIdle=”10 maxWait=”-1 defaultAutoCommit=”false” removeAbandoned=”true” removeAbandonedTimeout=”20″ />

NOTA: cuidado al copiar y pegar. La codificación de las comillas no es correcta y puede producirse un error al arrancar Tomcat. Si esto sucede borrar y volver a poner todas las comillas dobles.

De entre todos los parámetros caben destacar:

  • maxActive: El número máximo de conexiones activas que puede haber en el pool. Si se supera no se sirven más peticiones hasta que se libere alguna de las ocupadas.
  • maxIdle: El número máximo de conexiones que estarán esperando a ser asignadas. Deberá de ser menor que el anterior.
  • maxWait: El número de milisegundos que se esperará a que la aplicación devuelva la conexión antes de lanzar una excepción.
  • removeAbandoned: Activar/Desactivar la funcionalidad para que Tomcat considere automaticamente que una conexión ha sido abandonada.
  • removeAbandonedTimeout: El número de segundos que espera el pool para considerar una conexión como abandonada.

Y tenemos diferentes sitios donde incluirlo:

  • En el server.xml del servidor (no de la aplicación, está en la carpeta /tomcat/conf/) buscar la etiqueta (“context”) donde se declara el contexto de nuestra aplicación e incluirla como una subetiqueta. La edición de este fichero se puede hacer directamente desde Eclipse:
    1. Botón derecho sobre el nombre del proyecto, Propiedades -> Tomcat
    2. En la pestaña General dentro del área de texto “Información Extra” incluimos el texto donde se declara el recurso.
    3. Botón Aplicar y OK.
    4. Nuevamente botón derecho sobre el nombre del proyecto, Tomcat project -> Update context definition.
    5. Copiamos el .jar con el driver de MySQL en el directorio TOMCAT_HOME/lib.
  • En el context.xml del directorio META-INF de la aplicación. Si no existe el fichero se debe crear. A la hora de hacer prácticas o probar una aplicación tal vez sea la opción más sencilla ya que se puede compartir la configuración a la misma vez que exportas el proyecto; pero esto no es correcto en un entorno de producción ya que el desarrollador no debe conocer las credenciales de acceso a la base de datos.
  • Si está, en el fichero nombreAplicacion.xml que hay en /tomcat/conf/Catalina/localhost/.

Utilizar el recurso en la aplicación

Podemos crear una clase Singleton donde aparece este código:

Context context = new InitialContext();

DataSource dataSource = (DataSource) context.lookup(“java:comp/env/jdbc/miServidor”);

connection = dataSource.getConnection();

siendo “jdbc/miServidor” el texto que hemos puesto al declarar el recurso.

Cuando desde el modelo o en nuestro caso desde el Singleton se haga connection.close() la conexión quedará disponible para futuras peticiones.

O podemos incluir la siguiente definición en un servlet para que se haga la inyección del objeto de la clase DataSource a la que hace referencia el recurso:

@Resource(name=”jdbc/miServidor”)
private static DataSource dataSource;

En cualquier parte del servlet ya sólo nos queda recuperar la conexión con la base de datos de la misma forma que aparece en la última línea.

Etiquetas: , , ,

5 Respuestas a “Conectarse a MySQL en Tomcat”

  1. Isra dice:

    ¿Por qué hacer algo simple si se puede hacer complicado? Use Java, sea friki. jajaja

  2. frank dice:

    PORQUE NO UTILIZAR WAMP y solucionado todo!!!

  3. jesus dice:

    Buen aporte gracias. Pura casualidad sabes si con servlets se pueden hacer websockets

  4. Miguel Angel dice:

    El “problema” del websocket es que se trata de un protocolo que tiene que ser implementado tanto por el navegador como por el servidor.

    Cada vez son más los navegadores que lo soportan. En cuanto a la parte del servidor, y en el caso de Tomcat que es el conozco, a partir de la versión 7 se ha empezado a darle soporte.

    Mira esta referencia http://tomcat.apache.org/tomcat-7.0-doc/web-socket-howto.html

    Por lo visto van a crear un servlet “especial”…. WebSocketServlet

  5. Miguel Angel dice:

    Toda la razón 🙂

Dejar una respuesta