Pooling de Conexiones a base de datos con C3P0 y Java

Estándar

Un pool de conexiones a base de datos es una forma de llevar un control de las mismas de modo que al desocupar una conexión, en lugar de cerrar la conexión la tengamos a la mano y se entregue al siguiente objeto que necesite una conexión.

Si bien esto puede sonar innecesario debe de recordar que crear una conexión a base de datos es una de las operaciones mas pesadas que puede efectuar y que las conexiones expiran automáticamente después de un tiempo sin uso, ambas cosas que pueden afectar el rendimiento o estabilidad de su aplicación.

Ahí es donde entra el pool de conexiones ya que este se encargara de reducir el numero de veces que necesite crear una nueva conexión y en caso que esta expire crea una nueva si es necesario.

Pool de conexiones C3P0

Uno de los pools de conexiones mas populares actualmente es el C3P0 proporcionado por Machinery For Change, Inc, este le proporciona todas las funcionalidades esperadas a la vez que se integra con gran facilidad a otros frameworks de Java.

Debido a su popularidad y sencillez de uso nos enfocaremos en como hacer uso de este pool en particular.

Obtener las librerías de c3P0.

Vaya a la pagina oficial del proyecto: http://www.mchange.com/projects/c3p0/ o directamente al repositorio en SourceForge y descargue la versión mas reciente.

En el archivo .zip que descargo vaya a la carpeta lib y extraiga los archivos c3p0-0.9.5.2.jar, c3p0-oracle-thin-extras-0.9.5.2.jar y mchange-commons-java-0.2.11.jar con estos ya podrá usar el pool en su proyecto.

A su vez necesitara de la librería para usar conexiones a base de datos, agregue esta a su proyecto como se ve en la figura 2.

2016-04-18

Figura 1 – Librerías de C3P0.

2016-04-18 (1)

Figura 2 – Librerías del proyecto.

Estos archivos .jar pueden agregarse a un proyecto como ya lo hemos hecho con anterioridad.

Uso del pool C3P0 en un programa.

En el ejemplo que se utilizara para demostrar el uso del pool seguramente notará un par de cosas un tanto inusuales, específicamente que obtendremos el pool de conexiones vía un método getInstance en lugar de un constructor, obtendremos las conexiones desde el pool  y que haremos un uso constante del método close del objeto conexión.

El uso del getInstance es por una razón muy sencilla, haciendo el pool un objeto estático y obteniendo este vía el getInstance podemos tener el *mismo* pool disponible en todo el programa sin necesidad de estar pasando ese objeto a cada una de las clases, se los digo por experiencia, tener que agregar un parámetro extra a cada constructor y crear los getter y setter necesarios se vuelve aburrido de inmediato.

El obtener las conexiones desde el pool es necesario para poder obtener los beneficios de este, fuera de eso se comportan de manera usual por lo que no necesita modificar cómo realiza sus consultas.

Con respecto a llamar el método close esto es necesario para indicar al pool que ya no estamos usando esa conexión, de modo que esté disponible para cuando se solicite, es *vital* que recuerde llamar al método close o que use la estructura try-with-resources proporcionada por Java, ya que el no hacer esto puede trabar su aplicación, ese caso se verá en el ejemplo.

Ejemplo.

El ejemplo que se presentará es muy sencillo, pero presenta los detalles de como crear la clase para manejar el pool  y cómo usar esta en un programa de una forma que, espero, sea lo más legible y sencilla de entender posible.

El ejemplo consistirá en dos clases, una que crea y configura el pool y otra desde la cual realizaremos una serie de consultas a la tabla de base de datos que se ve en la figura 3.

2016-04-20 (2)

Figura 3 – Tabla a presentar.

package c3p0ejemplo;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Connection;

public class PoolC3P0 {
 // Notara que el pool es un miembro *estatico* esto es para evitar duplicidad
 private static PoolC3P0 datasource;
 // Esta es la fuente de datos que conecta con la base de datos
 private final ComboPooledDataSource cpds;

 /**
  * Crea el constructor del pool, notara que este constructor es privado
  * esto con el fin de que podamos controlar cuando se crea el pool
  * @throws IOException
  * @throws SQLException
  * @throws PropertyVetoException
  */
 private PoolC3P0() throws IOException, SQLException, PropertyVetoException {
  // Configuramos la conexion a base de datos
  // Creamos la fuente de datos
  cpds = new ComboPooledDataSource();
  // Que driver de base de datos usaremos
  cpds.setDriverClass("com.mysql.jdbc.Driver");
  // La url de la base de datos a la que nos conectaremos
  cpds.setJdbcUrl("jdbc:mysql://127.0.0.1/");
  // Usuario de esa base de datos
  cpds.setUser("usuario");
  // Contraseña de la base de datos
  cpds.setPassword("contrasena");

  // Configuramos el pool
  // Numero de conexiones con las que iniciara el pool
  cpds.setInitialPoolSize(0);
  // Minimo de conexiones que tendra el pool
  cpds.setMinPoolSize(0);
  // Numero de conexiones a crear cada incremento
  cpds.setAcquireIncrement(1);
  // Maximo numero de conexiones
  cpds.setMaxPoolSize(5);
  // Maximo de consultas
  cpds.setMaxStatements(180);
  // Maximo numero de reintentos para conectar a base de datos
  cpds.setAcquireRetryAttempts(2);
  // Que se genere una excepcion si no se puede conectar
  cpds.setBreakAfterAcquireFailure(true);
 }

 /**
  * Nos regresa la instancia actual del pool, en caso que no halla una instancia
  * crea una nueva y la regresa
  * @return
  * @throws IOException
  * @throws SQLException
  * @throws PropertyVetoException
  */
 public static PoolC3P0 getInstance() throws IOException, SQLException, PropertyVetoException {

  if (datasource == null) {
   datasource = new PoolC3P0();
   return datasource;
  } else {
   return datasource;
  }
 }

 /**
  * Este metodo nos regresa una conexion a base de datos, esta la podemos
  * usar como una conexion usual
  * @return Conexion a base de datos
  * @throws SQLException
  */
 public Connection getConnection() throws SQLException {
  return this.cpds.getConnection();
 }

}
package c3p0ejemplo;

import java.beans.PropertyVetoException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class C3P0Ejemplo {

 /**
  * @param args the command line arguments
  */
 public static void main(String[] args) {
  try {
   // Obtenemos el pool de conexiones
   PoolC3P0 pool = PoolC3P0.getInstance();

   for (int i = 0; i < 10; i++) {
    // Solicitamos una conexion al pool
    Connection cx = pool.getConnection();
    // Creamos nuestro objecto de consulta
    Statement consulta = cx.createStatement();
    // La consulta que ejecutaremos
    String sql = "SELECT * FROM pruebas.prueba";
    // Obtenemos los datos
    ResultSet data = consulta.executeQuery(sql);
    data.next();

    Integer puerto = data.getInt("puerto_serial");
    Integer impresora = data.getInt("puerto_impresora");
    Integer red = data.getInt("puerto_red");
    Integer scaner = data.getInt("puerto_escaner");

    // Los presentamos en pantalla
    System.out.println("Vuelta numero " + i);
    System.out.println("Puerto " + puerto);
    System.out.println("Impresora " + impresora);
    System.out.println("Red " + red);
    System.out.println("Escaner " + scaner);
    System.out.println("");
    System.out.println("");

    // Cerramos la conexion, esto es vital, de no hacerlo el pool creara una nueva conexion
    // pero al cerrar liberamos esa conexion para que el pool la reuse
    cx.close();

    // Un retrazo de 10 segundos para poder ver las conexiones en MySQL Workbench
    Thread.sleep(5000);
   }

  } catch (IOException ex) {
   System.out.println("Error de entrada salida");
  } catch (SQLException ex) {
   System.out.println("Error de conexion a base de datos");
  } catch (PropertyVetoException ex) {
   System.out.println("Error de propiedades");
  } catch (InterruptedException ex) {
   System.out.println("Error de interrupcion de proceso");
  }

 }

}

Ya que ejecute el programa vera que el programa abre una conexión, consulta, presenta los datos, realiza una espera y cierra la conexión, el resultado se vera como la figura 4.

2016-04-20 (1)

Figura 4 – Salida del programa

Como ve el ejemplo pide y cierra conexiones a cada vuelta, mientras el programa se ejecuta acceda a la administración de conexiones de usuario en workbench, notara que la conexión que usamos (identificada por el usuario “usuario”) nunca cambia, no importa cuantas conexiones abramos o cerremos solo se esta usando una como vemos en la figura 5.

2016-04-20 (3)

Figura 5 – Conexiones del usuario

Como un extra modifique la tabla mientras corre el programa y vera que los datos presentados cambian en forma acorde.

Espero que la entrada fuera útil y nos vemos en la próxima.