Ejemplos java y C/linux

Google
 

Tutoriales

Enlaces

Licencia

Creative Commons License
Esta obra está bajo una licencia de Creative Commons.
Para reconocer la autoría debes poner el enlace http://www.chuidiang.com

Pool de conexiones: BasicDataSource

Hemos visto en mysql-java-basico que podemos establecer la conexión con la base de datos por medio de la clase DriverManager.getConnection(). De esta forma obtenemos una Connection real con la base de datos, es decir, al llamar a este método, se establece la conexión con la base de datos y cuando llamemos al método connection.close(), dicha conexión se cierra.

Sin embargo, hay un pequeño problema con esto. Varios hilos no pueden usar una misma conexión física con la base de datos simultáneamente, ya que la información enviada o recibida por cada uno de los hilos se entremezcla con la de los otros, haciendo imposible una escritura o lectura coherente en dicha conexión.

Hay varias posibles soluciones para este problema:

  1. Abrir y cerrar una conexión cada vez que la necesitemos. De esta forma, cada hilo tendrá la suya propia. Esta solución en principio no es eficiente, puesto que establecer una conexión real con la base de datos es un proceso costoso. Andar abriendo y cerrando conexiones con frecuencia puede hacer que nuestro programa vaya más lento de lo debido.
  2. Usar una única conexión y sincronizar el acceso a ella desde los distintos hilos. Esta solución es más o menos buena, pero requiere cierta disciplina al codificar, ya que tendremos que acordarnos de poner siempre los synchronized antes de hacer cualquier transacción con la base de datos. También tiene la pega de que los hilos deben esperar unos por otros
  3. Finalmente, podemos tener varias conexiones abiertas, de forma que cuando un hilo necesite una, la "reserve" para su uso y cuando termine, la "libere" para que pueda ser usada por los demás hilos, todo ello sin abrir y cerrar la conexión cada vez. De esta forma, si hay conexiones disponibles, un hilo no tiene que esperar a que otro acabe. Esta solución es en principo la ideal y es lo que se conoce como un pool de conexiones. Afortunadamente, no debemos preocuparnos de codificarlo nosotros mismos, ya que los hay disponibles en internet. Esta es la solución que vamos a detallar en este tutorial.

 

Pool de conexiones

En java, un pool de conexiones es una clase java que tiene abiertas varias conexiones a base de datos. Cuando alguien necesita una conexión a base de datos, en vez de abrirla directamente con DriverManager.getConnection(), se la pide al pool usando su método pool.getConnection(). El pool coge una de las conexiones que ya tiene abierta, la marca como que alguien la está usando para no dársela a nadie más y la devuelve. La siguiente llamada a este método pool.getConnection(), buscará una conexión libre para marcarla como ocupada y la devolverá ... y así sucesivamente.

Cuando el que ha pedido la conexión termina de usarla, normalmente después de una transacción con la base de datos o varias seguidas, llama al método connection.close(). Esta conexión que nos ha sido entregada por el pool, realmente no se cierra con esta llamada. El método close() únicamente avisa al pool que ya hemos terminado con la conexión, de forma que sin cerrarla, la marca como libre para poder entregársela a otro que lo pida.

El uso del pool y de la conexión, por tanto, sigue el siguiente esquema

// Se le pide al pool una conexion libre
Connection conexion = pool.getConnection();

// Se hace una o más transacciones: select, update, insert, delete ...

// Se libera la conexión para su posible uso por otro hilo
conexion.close();

apache dbcp : BasicDataSource

En la librería de apache commons-dbcp tenemos una implementación sencilla de un pool de conexiones. Dicha librería es commons-dbcp, que te puedes descargar del enlace. Esta librería necesita a su vez la librería commons-pool, por lo que debemos descargarnos también esta. Una vez descargadas esas dos librerías y el driver de nuestra base de datos mysql-connector-java-5.1.6.jar para nuestro ejemplo, debemos añadir todas ellas como dependencias de nuestro proyecto o al CLASSPATH del mismo.

Una vez configurado todo, para usar BasicDataSource no tenemos más que hacer un new de esa clase y pasarle los parámetros adecuados de nuestra conexión con los métodos set() disponibles para ello. El siguiente trozo de código muestra cómo hacerlo

BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUsername("hibernate");
basicDataSource.setPassword("hibernate");
basicDataSource.setUrl("jdbc:mysql://localhost/hibernate");

// Opcional. Sentencia SQL que le puede servir a BasicDataSource
// para comprobar que la conexion es correcta.
basicDataSource.setValidationQuery("select 1");

Símplemente hemos hecho el new de la clase y le hemos pasado:

Estas cuatro son las estrictamente necesarias. BasicDataSource permite configurar muchas otras cosas, como cuantas conexiones debe abrir como máxima, cuántas puede tener abiertas que no se estén usando, etc. Y una característica interesante, se le puede pasar una consulta que BasicDataSource ejecutará para comprobar que la conexión con la base de datos es correcta. Esta consulta se pasa con setValidationQuery() y en el caso del ejemplo es un simple "select 1", que siempre devolverá resultado independientemente de las tablas y datos existentes en la base de datos.

Una vez instanciado el BasicDataSource y para evitar que dentro de lo posible el resto de nuestro código dependa de este pool de conexiones concreto, se suele asignar el BasicDataSource a una variable de tipo javax.sql.DataSource, que es una interface estándar de java.

DataSource dataSource = basicDataSource;
// la variable dataSource es la que debería ver el resto de nuestro programa.

Una vez guardado nuestro dataSource en una variable de tipo DataSource, el código para hacer una transacción con la base de datos debe serguir este esquema:

Connection conexion = dataSource.getConnection();
// Hacer el select, insert, update, delete...
conexion.close();

Por ejemplo, si suponemos una tabla Person con cuatro campos id, edad, nombre y apellido, para hacer una inserción haríamos lo siguiente:

Connection conexion = null;
try {
   // BasicDataSource nos reserva una conexion y nos la devuelve.
   conexion = dataSource.getConnection();

   // La insercion.
   Statement ps = conexion.createStatement();
   ps.executeUpdate(
      "insert into person values (null,22,'Pedro','Martinez')");
} catch (SQLException e) {
   e.printStackTrace();
} finally {
   // En realidad no cierra la conexion, sino que avisa al pool de que
   // esta conexión queda libre.
   conexion.close();
}

Hemos puesto el conexion.close() en el finally del try-catch, para asegurarnos de que la conexion se cierra independientemente de que todo vaya bien o salten excepciones. Realmente y por claridad, hemos hecho una pequeña trampa en el finally: No podemos cerrar la conexion tal cual se está haciendo y deberíamos, por un lado, verificar que conexion no es null antes de intentar cerrar y por otro lado, close() lanza una excepción que debemos capturar. El código real del finally para una cosa tan sencilla como llamar a un close() se complica así

finally {
   try {
      if (null != conexion) {
         conexion.close();
      }
   } catch (SQLException e) {
      e.printStackTrace();
}

En EjemploDataSource.java tienes un ejemplo completo usando BasicDataSource.

Configurar el pool desde un fichero de propiedades: BasicDataSourceFactory

Si no queremos poner todos los parámetros necesarios para la conexión directamente en código y usando los métodos set() mencionados anteriormente, podemos poner dichos parámetros en un fichero de propiedades de java datasource_config.properties, similar al siguiente

# Propiedades para la conexion a base de datos con BasicDataSource
driverClassName=com.mysql.jdbc.Driver
username=hibernate
password=hibernate
url=jdbc:mysql://localhost/hibernate
validationQuery=select 1

en el que el nombre de cada propiedad se extrae del método set correspondiente, quitando set y pasando la primera letra a minúscula (BasicDataSource no es más que un bean de java, con métodos set() y get() para sus propiedades). Ahora sólo nos queda cargar este fichero de propiedades en nuestro código

Properties propiedades = new Properties();
propiedades.load(new FileInputStream(
   "src/main/config/datasource_config.properties"));

siendo java.util.Properties una clase estándar de java.

Para obtener nuestro BasicDataSource a partir de estas propiedades cargadas, debemos usar el método createDataSource() de la clase BasicDataSourceFactory que viene con commons-dbcp. El código para ello puede ser como este

DataSource dataSource = BasicDataSourceFactory.createDataSource(propiedades);

El resto, cómo realizar una transacción, es igual que el caso anterior. Tienes un ejemplo completo en EjemploBasicDataSourceFactory.java.

Ahora puedes pasar a mysql-java-basico.

 

Estadísticas y comentarios

Numero de visitas desde el 4 Feb 2007: