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.

Anuncios

Coloreando las celdas, filas y columnas de una tabla JTable en Java

Estándar

Debido que una tabla de datos puede llegar a ser visualmente tediosa, dificil de seguir debido a su tamaño o que deseé resaltar ciertos valores significativos es posible que necesite colorear ciertas celdas, filas, columnas o cualquier combinación de estas en forma especifica, aqui presentaremos como.

TableCellRenderer
Cada objecto JTable cuenta con un objecto una clase que implemente la interfaz TableCellRenderer, mismo que se encarga de controlar como se renderiza cada celda que componga la JTable via el metodo getTableCellRendererComponent, como seguro ya dedujo, si deseamos controlar los colores de las celdas de la tabla necesitamos una clase que implemente la interfaz TableCellRederer y defina el método getTableCellRendererComponent para que haga lo que necesitamos, no se asuste por complejo que esto suene (y que algunos de esos metodos tienen una cantidad extrañamente larga de argumentos) esto es muy sencillo y lo veremos a continuación.

Implementando la interfaz TableCellRenderer.
Esta interfaz es muy sencilla, solo agrege implements TableCellRenderer despues del nombre de la clase y agrege el método getTableCellRendererComponent, como se ve en el código a continuación.

package colortable;

import java.awt.Color;
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

/**
 *
 * @author darta_000
 */
public class Resaltador implements TableCellRenderer {
    private Integer fila;
    public static final DefaultTableCellRenderer DEFAULT_RENDERER = new DefaultTableCellRenderer();
    
    /**
     * Creamos el resaltador indicando que fila se coloreara por defecto
     * @param row 
     */
    public Resaltador(Integer row){
        fila = row;
    }
    
    /**
     * Colorea la celda si pertenece a la fila indicada, esta funcion es llamada internamente por la tabla
     * que use esta clase como renderizados
     * @param table Tabla
     * @param value Valor de la celda
     * @param isSelected Celda selecionada
     * @param hasFocus Celta tiene el foco
     * @param row Fila de la celda
     * @param column Columna de la celda
     * @return Celda de la tabla
     */    
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        // Obtenemos la celda que se esta renderizando
        Component c = DEFAULT_RENDERER.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

        // Si la celda esta en la fila indicada y no esta seleccionada se coloreara de este modo        
        if (fila.compareTo(row) == 0 && isSelected == false) {
            c.setBackground(Color.decode("#FF88FF"));
            c.setForeground(Color.BLACK);
        // Si la celda esta en la fila indicada y esta seleccionada se coloreara de este modo
        } else if (fila.compareTo(row) == 0 && isSelected == true) {
            c.setBackground(Color.RED);
            c.setForeground(Color.WHITE);
        // Si la celda no esta en la fila indicada y esta seleccionada se coloreara de este modo
        } else if (fila.compareTo(row) != 0 && isSelected == true) {
            c.setBackground(Color.BLUE);
            c.setForeground(Color.WHITE);
        // En los demas casos se coloreara de este modo
        } else {
            c.setBackground(Color.WHITE);
            c.setForeground(Color.BLACK);
        }
        // Regresamos la celda para que se agrege a la tabla
        return c;
    }

    /**
     * @return the fila
     */
    public Integer getFila() {
        return fila;
    }

    /**
     * @param fila the fila to set
     */
    public void setFila(Integer fila) {
        this.fila = fila;
    }
}

Notara que el método getTableCellRendererComponent tiene varios argumentos que hace cada uno se describe a continuación:

  • JTable table – La tabla sobre la que opera el metodo
  • Object value – El valor a presentar en esa celda
  • boolean isSelected – Le indicara si esa celda esta seleccionada
  • boolean hasFocus – Le indica si esa celda tiene el foco de edición
  • int row – Indica el numero de fila en que se encuentra la celda
  • int colum – Indica el numero de columna en que se encuntra la celda.

Antes que se pregunte donde va a sacar esos parametros recuerde que esa función es usada internamente por el JTable, esos argumentos estan ahi para que pueda identificar si una celda debe de ser rendereizada en forma especial, como vimos en la clase que creamos previamente

Ejemplo
Para este ejemplo haremos algo sencillo, crearemos una tabla, la cual llenaremos con datos, via una entrada de texto diremos que fila queremos colorear y con un boton refrescaremos la tabla

package colortable;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;

/**
 *
 * @author darta_000
 */
public class ColorTable extends JFrame implements ActionListener {

    private final JTable tabla;
    private final JScrollPane barras;
    private final DefaultTableModel modelo;
    private final JTextField entrada;
    private final JButton boton;
    private final String[] columnas = {"Indice", "Dato"};
    Resaltador resaltado;

    /**
     * Crea la ventana con la tabla
     */
    public ColorTable() {
        entrada = new JTextField();
        boton = new JButton("Resaltar");
        modelo = new DefaultTableModel();
        tabla = new JTable();
        barras = new JScrollPane(tabla);
        resaltado = new Resaltador(0);

        // Detalles de la ventana
        this.setTitle("Tabla Color");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(new BorderLayout());

        // Establecemos el modelo de la tabla.
        modelo.setColumnIdentifiers(columnas);
        tabla.setModel(modelo);

        //Colocamos los elementos en su lugar
        this.getContentPane().add(barras, BorderLayout.CENTER);
        this.getContentPane().add(entrada, BorderLayout.NORTH);
        this.getContentPane().add(boton, BorderLayout.SOUTH);

        // El evento del boton
        boton.addActionListener(this);

        // Iniciamos el valor del texto
        entrada.setText("0");

        // Indicamos como sera el resaltado de la tabla
        tabla.setDefaultRenderer(Object.class , resaltado);

    }

    /**
     * Limpia y agrega los datos en la tabla
     */
    private void agregarDatos() {
        String[] datos = {"CERO", "Uno", "Dos", "Tres", "Cuatro", "Cinco", "Seis", "Siete", "Ocho", "Nueve", "Diez", "Once"};

        modelo.setRowCount(0);

        for (int i = 0; i < datos.length; i++) {
            Object[] row = {i, datos[i]};
            modelo.addRow(row);
        }
    }

    /**
     * Muestra la ventana en pantalla
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        ColorTable color = new ColorTable();

        color.pack();
        color.setVisible(true);
    }

    /**
     * Lo que se realiza cuando se presiona el boton
     * @param e Evento
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        Integer fila;
        try {
            fila = Integer.parseInt(entrada.getText());
        } catch (NumberFormatException ex) {
            fila = 0;
        }

        resaltado.setFila(fila);

        this.agregarDatos();
    }
}

Al momento de ejecutar el programa vera algo como en la figura 1.

01_inicio

Figura 1 – Programa recien iniciado.

Presione el boton resaltar y vera que la fila cero se colorea.

02_color_0

Figura 2 – Fila cero coloreada.

Escriba el numero de alguna fila en la entrada de texto, presione el boton y vera como esa fila se colorea, cambie el numero y vera otra fila cambiar.

03_nueve

Figura 3 – Cambiando la fila coloreada.

El coloreado puede ser cambiando dinamicamente, y esto se aplicara cada vez que se actualice la tabla.