Implementación de un Pool de Conexiones JDBC con C3P0

Concepto fundamental

C3P0 es una biblioteca de código abierto que implementa un pool de conexiones JDBC, ampliamente adoptada en frameworks como Hibernate y Spring. Su propósito principal es gestionar de manera eficiente las conexiones a bases de datos, evitando la sobrecarga de crear y destruir conexiones constantemente.

Un pool de conexiones opera bajo el principio de reutilización: se establece un conjnuto predeterminado de conexiones disponibles que las aplicaciones pueden tomar y devolver según sea neecsario. Esto reduce significativamente la latencia asociada a la inicialización de conexiones, que suele ser más costosa que la ejecución de las propias sentencias SQL. Sin un mecanismo de pooling, cada soliciutd de la aplicación requeriría negociar una nueva conexión con el servidor de base de datos, lo cual implica un alto consumo de recursos.

Configuración inicial

Dependencias requeridas

Es necesario incluir en el proyecto las bibliotecas de C3P0 y el conector JDBC correspondiente al motor de base de datos utilizado (por ejemplo, MySQL Connector/J).

Archivo de configuración XML

C3P0 busca un archivo denominado c3p0-config.xml en el classpath. A continuación se muestra un ejemplo básico de configuración:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
  <default-config>
    <!-- Parámetros del conector de base de datos -->
    <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/bd_aplicacion?useSSL=false</property>
    <property name="user">admin</property>
    <property name="password">clave_segura123</property>

    <!-- Dimensionamiento del pool -->
    <property name="maxPoolSize">20</property>
    <property name="minPoolSize">5</property>
    <property name="initialPoolSize">10</property>
  </default-config>
</c3p0-config>

Uso en código Java

Para obtener conexiones desde el pool, se instancia un ComboPooledDataSource que carga automáticamente la configuración del archivo XML:

import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class GestorConexiones {

    private static final ComboPooledDataSource fuenteDatos = new ComboPooledDataSource();

    public static Connection obtenerConexion() throws SQLException {
        return fuenteDatos.getConnection();
    }

    public static void cerrarRecursos(Connection conn, PreparedStatement ps, ResultSet rs) {
        try { if (rs != null) rs.close(); } catch (SQLException ignored) {}
        try { if (ps != null) ps.close(); } catch (SQLException ignored) {}
        try { if (conn != null) conn.close(); } catch (SQLException ignored) {}
    }

    public static void liberarPool() {
        fuenteDatos.close();
    }
}

Ejemplo de consulta utilizando el pool:

public class EjemploConsulta {

    public void mostrarUsuarios() {
        String sql = "SELECT id, nombre, correo FROM usuarios WHERE activo = ?";
        Connection conexion = null;
        PreparedStatement sentencia = null;
        ResultSet resultados = null;

        try {
            conexion = GestorConexiones.obtenerConexion();
            sentencia = conexion.prepareStatement(sql);
            sentencia.setBoolean(1, true);
            resultados = sentencia.executeQuery();

            while (resultados.next()) {
                long id = resultados.getLong("id");
                String nombre = resultados.getString("nombre");
                String correo = resultados.getString("correo");
                System.out.printf("ID: %d | Nombre: %s | Correo: %s%n", id, nombre, correo);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            GestorConexiones.cerrarRecursos(conexion, sentencia, resultados);
        }
    }
}

Parámetros avanzados de configuración

El archivo XML de C3P0 permite ajustar múltiples propiedades que afectan el comportamiento y rendimiento del pool:

<c3p0-config>
  <default-config>

    <!-- Incremento de conexiones solicitadas al pool cuando se agotan. Por defecto: 3 -->
    <property name="acquireIncrement">5</property>

    <!-- Intentos de reconexión tras fallo al obtener conexión. Por defecto: 30 -->
    <property name="acquireRetryAttempts">30</property>

    <!-- Espera en milisegundos entre reintentos. Por defecto: 1000 -->
    <property name="acquireRetryDelay">1500</property>

    <!-- Si es true, las operaciones pendientes se revierten al cerrar conexión. Por defecto: false -->
    <property name="autoCommitOnClose">false</property>

    <!-- Tabla vacía que C3P0 creará para pruebas internas de conectividad -->
    <property name="automaticTestTable">conexion_prueba</property>

    <!-- Si es true, el datasource se marca como inactivo tras fallo de conexión. Por defecto: false -->
    <property name="breakAfterAcquireFailure">false</property>

    <!-- Tiempo máximo de espera para obtener una conexión del pool (ms). 0 = sin límite. Por defecto: 0 -->
    <property name="checkoutTimeout">30000</property>

    <!-- Período de verificación de conexiones inactivas (segundos). Por defecto: 0 -->
    <property name="idleConnectionTestPeriod">120</property>

    <!-- Conexiones creadas al inicializar el pool. Debe estar entre minPoolSize y maxPoolSize -->
    <property name="initialPoolSize">8</property>

    <!-- Tiempo máximo de inactividad antes de descartar una conexión (segundos). 0 = nunca. Por defecto: 0 -->
    <property name="maxIdleTime">300</property>

    <!-- Tamaño máximo del pool. Por defecto: 15 -->
    <property name="maxPoolSize">30</property>

    <!-- Tamaño mínimo del pool. Por defecto: 3 -->
    <property name="minPoolSize">10</property>

    <!-- Total de PreparedStatements cacheados. 0 = desactivado. Por defecto: 0 -->
    <property name="maxStatements">200</property>

    <!-- PreparedStatements cacheados por conexión individual. Por defecto: 0 -->
    <property name="maxStatementsPerConnection">20</property>

    <!-- Hilos auxiliares para operaciones asíncronas de C3P0. Por defecto: 3 -->
    <property name="numHelperThreads">5</property>

    <!-- Sentencia SQL preferida para pruebas de conectividad. Ignorada si se define automaticTestTable -->
    <property name="preferredTestQuery">SELECT 1</property>

    <!-- Si es true, valida la conexión al retirarla del pool. Impacta rendimiento. Por defecto: false -->
    <property name="testConnectionOnCheckout">false</property>

    <!-- Si es true, valida la conexión al devolverla al pool. Por defecto: false -->
    <property name="testConnectionOnCheckin">true</property>

    <!-- Intervalo en segundos para ciclos de revisión de configuración. Por defecto: 300 -->
    <property name="propertyCycle">300</property>

    <!-- Usuario de conexión a la base de datos -->
    <property name="user">admin</property>

    <!-- Contraseña de conexión a la base de datos -->
    <property name="password">clave_segura123</property>

  </default-config>

  <!-- Configuración con nombre para escenarios específicos -->
  <named-config name="poolReportes">
    <property name="maxPoolSize">10</property>
    <property name="minPoolSize">2</property>
    <property name="maxStatements">50</property>
  </named-config>
</c3p0-config>

Para utilizar una configuración con nombre específica:

ComboPooledDataSource fuenteReportes = new ComboPooledDataSource("poolReportes");

Etiquetas: C3P0 JDBC Connection Pool java MySQL

Publicado el 7-2 18:07