miércoles, 1 de febrero de 2012

Apache Commons DbUtils: sencillo, rápido, ligero


“Controlling complexity is the essence of computer programming”
Brian Kernigan

Hoy traigo aqui otra de las joyas de Apache Commons: DbUtils. Ya comenté en el artículo anterior "Librerías imprescindibles en Java" que, en cualquier projecto, antes de pensar en hacer nuestras utilities, debemos pasar por la página de Commons para ver las librerías existentes antes de ponernos a reinventar la rueda. Pues bien, Commons tiene otra pequeña joya que he descubierto y aplicado recientemente con éxito.

Hay ocasiones en las que no usamos un framework de mapeo tipo ORM (JPA/EclipseLink, Hibernate, Mybatis -antiguo iBATIS-) por varias razones: modelo muy cambiante, proyecto muy pequeño, necesitamos un control más fino de las consultas, generación dinámica, etc... En estos casos, no nos queda más remedio que usar directamente JDBC. Sin embargo JDBC es un API muy engorroso que requiere mucho código de limpieza de recursos y mucha fontanería a través de sus capas. Más del 80% de nuestro código JDBC es la instanciación de objetos del API, establecimiento de parámetros, liberación y cierre.


DbUtils es un pequeño conjunto de clases diseñadas para hacer mucho más fácil el trabajo que hacemos con JDBC centrándonos en lo que queremos: consultar, actualizar, insertar. Especialmente nos ayuda en:
  • Se encarga automáticamente de la fontanería previa (conexión y preparación) y de la limpieza, evitando posibles errores de liberación de recursos.
  • Rellena las propiedades de JavaBean automáticamente a partir de ResultSets. No es necesario copiar manualmente valores en instancias de beans.
Por todo la anterior, el uso de DbUtils reduce drásticamente la cantidad de código que necesitamos con JDBC. Por hacer una comparativa, yo díría que DbUtils es a JDBC lo que JDOM a javax.xml.

Usar DbUtils es muy sencillo. Hay dos clases/interfaces principales que constituyen el corazón de DbUtils: QueryRunner y ResultSetHandler. QueryRunner se encarga de realizar todo el trabajo: abre la conexión, ejecuta la consulta o actualización y devuelve el resultado. Para devolver el resultado de diferentes formas se usan las distintas implementaciones de la clase ResultSetHandler, que permite devolver el resultado en una lista de  POJO's, lista de arrays, lista de Map, etc...

A continuación pongo un ejemplo simple de una pequeña DAO de una aplicación que tuviese que acceder a una base de datos PostgreSQL y a un AS/400 simultáneamente. En el ejemplo se pueden observar características muy importantes de ahorro de código y, por tanto, de su consecuencia más valiosa, ahorro de tiempo:
  • No hay declaraciones de Connection, Statement y ResultSet.
  • No hay liberación de recursos (DbUtil lo hace automáticamente)
  • No es necesaria la engorrosa recolección de campos a través de la iteración del ResultSet. Podemos devolver las consultas en un Map<String, Object> (NombreCampo, Valor) o en un JavaBean cuyas propiedades coincidan con los campos que devuelva nuestra consulta.


/**
   Suponemos una clase DAO cualquiera de acceso a datos
*/
public class ASDao {
   
   private AS400JDBCDataSource asDataSource;
   private PGSimpleDataSource pgDataSource;
   private QueryRunner qr;

   public ASDao() throws Exception {
       // El datasource puede instanciarse...
       asDataSource = new AS400JDBCDataSource("host4.cestel.es","asuser","xxxxxx");
       // U obtenerse a partir de un JNDI de un contenedor
       pgDataSource = new InitialContext().lookup("java:comp/env/jdbc/pgDS");
       qr = new QueryRunner(asDataSource);
       qrp = new QueryRunner(pgDataSource);
   }


   /**
       Realiza una consulta parametrizada (que debe devolver una sóla fila) y 
       devuelve un Map<String,Object> con el nombre del campo
       y su valor. Se asume que la consulta devuelve una sóla fila.
       Son apenas dos líneas.
   */
   public Map<String,Object> findData(String clientCode, String debtCode) 
                                                                throws SQLException {
       MapHandler result = new MapHandler();
       return qrp.query(
               "select c.id, c.code, de.id, de.customer_debt_id, dos.id, dos.code\n" +
               "from debt de join dossier dos on de.dossier_id = dos.id \n" + 
               "join customer c on dos.customer_id = c.id\n" +
               "where customer_debt_id = ?\n" +
               "and c.code=?\n" +
               "and end_process_date is null", result,debtCode, clientCode);
   }

   /**
       Realiza una consulta parametrizada y devuelve el resultado como una lista de 
       JavaBeans "TablaAS". La creación de la lista y "llenado" de los objetos corre 
       por su cuenta. Lo único que tenemos que hacer es crear la clase TablaAS con 
       propiedades (getter, setter). Las propiedades de los POJO (JavaBeans) que 
       se "llenaran" serán aquellas cuyo nombre coincida con el campo devuelto.
       Dos líneas (más la clase)
   */
   public List<TablaAS> findByClient(String clientCode) throws SQLException {
       ResultSetHandler<list<TablaAS>> blh = new BeanListHandler<TablaAS>(TablaAS.class);
       return qr.query("select * from  CESQ.TABLA_AS where CODCLTE = ?", blh,clientCode);
   }

   /**
       Ejemplo de la misma consulta devuelto como un array 
       o como una lista de POJO's (JavaBean)
   */
   public void test() throws SQLException {
       ArrayHandler ah = new ArrayHandler();
       ResultSetHandler<list<TablaAS>> h = 
                       new BeanListHandler<TablaAS>(TablaAS.class);
       
       Object[] resultArray = 
           qr.query("select * from  CESQ.TABLA_AS FETCH FIRST 2 ROWS ONLY", ah);

       List<TablaAS> result = 
           qr.query("select * from  CESQ.TABLA_AS FETCH FIRST 2 ROWS ONLY", h);

   }
}

Como todas las cosas, obviamente DbUtils tiene un target concreto de aplicación, no es una bala de plata para todo. Ahora bien, si no devolvemos conjuntos de datos grandes, podría usarse como perfecto sustituto de JDBC con un mínimo impacto en el rendimiento y con una importante mejora en legibilidad y mantenibilidad.

Puedes ver más ejemplos en el sitio de DbUtils.


Referencias y más información:

3 comentarios :

  1. Buenas tardes, llevo como ocho años en la profesión, la mayor parte del tiempo en desarrollo java y en gestión pero los dos últimos años en sistemas windows, me considero aprendiz de muchas cosas y maestro en ninguna. Ahora me voy a embarcar en un proyecto profesional en el que voy a llevar todas las riendas de desarrollo (estoy solo, jaja) y necesito aprender rápido y bien, siendo muy práctico.

    Sólo pongo este comentario porque quería agradecerte tu trabajo, me estoy leyendo cada entrada del blog con auténtica pasión. Sintetizas muy bien y espero que sigas haciéndolo.

    Un abrazo,
    David.

    ResponderEliminar
    Respuestas
    1. Muchas gracias David por tus amables palabras. Comentarios como el tuyo son los que me animan a seguir escribiendo: es agradable saber que tus esfuerzos son de interés u utilidad para los demás.

      Mucho ánimo con ese nuevo proyecto y espero que el blog mantenga tu interés para que sigas volviendo por aquí.

      Un abrazo.

      Eliminar
    2. Hola me sirvió mucho el ejemplo para conectarse con DBUtils a través de JNDI, exactamente esta linea:

      pgDataSource = new InitialContext().lookup("java:comp/env/jdbc/pgDS");

      MUCHAS GRACIAS !

      Eliminar

Related Posts Plugin for WordPress, Blogger...
cookieassistant.com