lunes, 7 de octubre de 2013

Spring: Como manejar Transacciones y sus beneficios.

Recorriendo Spring
El tratar transacciones con Spring es una de las características más utilizadas por los proyectos de programación y aun así continúa siendo una de las menos conocidas y sobre las que hay poca aplicación, en lo personal creo que es por falta de conocimiento.

Una transacción de base de datos es un conjunto de instrucciones que se ejecutan en bloque. Por ejemplo, hago una consulta, modifico un registro A en la base de datos y elimino un registro B. Si en alguna de estas instrucciones se produce un error todo el proceso se echa atrás. De esta manera si luego consulto la base de datos veré que el registro A no ha sido alterado. 


Este proceso de “tirar atrás” las instrucciones realizadas se le dice hacer unrollback, mientras que el proceso de confirmar todas las instrucciones en bloque una vez hemos visto que no se ha producido ningún error se le llama hacer un commit.



Las transacciones se empiezan y terminan a nivel de servicio, nunca a nivel de DAO. Si lo pensamos tiene lógica, el servicio es el que se encarga de gestionar toda la lógica de negocios: llamará a los DAOs que necesite para consultar, guardar o modificar registros y lo ha de hacer de manera atómica. 



Por ejemplo, supongamos un proceso batch que trabaja por la noche y que se encarga de recalcular la hipoteca de los clientes. 
  • En el primer paso obtiene el registro de la hipoteca
  • El segundo recalcula la hipoteca
  • El tercero actualiza el registro del cliente con una marca para señalar que el proceso ya ha procesado ese cliente. 

Nota: El DAO se encarga de la consulta, de la actualización de la hipoteca y de la actualización del registro con la marca de manera individual, primero con un método que lance una query, después con un update de la hipoteca y posteriormente con un update de la tabla de avisos del cliente. 

Pero es el servicio, o su equivalente batch, el que se encarga de que todo este proceso se trate como una unidad. Si empezase la transacción a nivel del DAO no tendría manera de controlar la atomicidad de todo el proceso.


Una transacción es descrita tradicionalmente en desarrollo de software por medio de cuatro factores (ACID)

  • Atómico: Las transacciones están compuestas de una o más actividades agrupadas como una sola unidad de trabajo. Este factor asegura que todas las operaciones en una transacción ocurrirá si la todas las operaciones terminan su ejecución correctamente o ninguna de ellas ocurrirá si cualquiera de estas actividades termina su ejecución incorrectamente, en cuyo caso se deshacen todos los cambios efectuados por las operaciones de la transacción.
  • Consistente: Una vez que una transacción termina su ejecución, ya sea correcta o incorrectamente, el sistema es dejado en un estado consistente con el negocio que modela.
  • AisladoLas transacciones debe permitir a varios usuarios trabajar con los mismos datos, sin que cada trabajo de un usuarios se enrede con el de los demás. Por lo tanto, las transacciones deben ser aisladas unas de las otras, previendo de esta manera la lectura y/o escritura concurrente de los datos.
  • Durable: Una vez que una transacción concluye, el resultado de una transacción debe persistir y debe sobrevivir a cualquier tipo de fallo del sistema.

Spring provee soporte para administración de transacciones declarativas y programables, además estas no requieren implementar JTA, esto es, si la aplicación utiliza solamente un simple recurso persistente, Spring puede utilizar el soporte transaccional ofrecido por el mecanismo de persistencia, como JDBC, Hibernate o JPA, pero si la aplicación utiliza varios recursos, Spring soporta transacciones distribuidas, XA, utilizando alguna implementación JTA de terceros.

La administración de transacciones programables ofrecen la flexibilidad y control más preciso de definir los limites de la transacción en el código, mientras que las transacciones declarativas, basadas en Spring AOP, ayudan a desacoplar una operación de las reglas de la transacción. Que enfoque utilizar es una decisión de control contra conveniencia.

Spring no administra las transacciones directamente si no que esta incorporado con una selección de administradores de transacciones para delegar esta responsabilidad a la implementación de la plataforma especifica provista ya sea por JTA o por el mecanismo de persistencia, cada uno de estos mecanismos actúa como un facade y son listados a continuación:





  • org.springframework.jca.cci.connection.CciLocalTransactionManager
  • org.springframework.jdbc.datasource.DataSourceTransactionManager
  • org.springframework.jms.connection.JmsTransactionManager
  • org.springframework.jms.connection.JmsTransactionManager102
  • org.springframework.orm.hibernate3.HibernateTransactionManager
  • org.springframework.orm.jdo.JdoTransactionManager
  • org.springframework.orm.jpa.JpaTransactionManager
  • org.springframework.transaction.jta.JtaTransactionManager
  • org.springframework.transaction.jta.OC4JJtaTransactionManager
  • org.springframework.transaction.jta.WebLogicJtaTransactionManager
  • org.springframework.transaction.jta.WebSphereUowTransactionManager


  • Transacciones con Spring

    La manera más común de tratar las transacciones en Spring es mediante la anotación@Transactional en la cabecera del método de una clase (nunca un interfaz) gestionada por Spring:
    @Transactional
    public void hazAlgoTransaccionalmente() {
        // Soy transaccional!
    }
    Nota: la anotación sólo funcionará en métodos públicos desde los cuales se acceda a la clase. Pero si definimos un metodo privado y aplicamos la notación para transacciones que sucede ?.  Lo peor no es que no funcione, lo peor es que no funcionará y no te avisará de que te estás equivocando. Y es que la anotación @Transactional es tratada como un metadato, si lo puede aprovechar lo hace pero si no lo ignora, por decirlo de una forma seriamos locos pensando que el metodo esta transaccionando, cuando no estaria funcionando y lo peor es que nos daremos cuenta tarde. 
    La regla es simple, la anotación sólo tiene efecto en la cabecera de un método por el cual se acceda a la clase. Evidentemente se puede marcar todos los métodos como transaccionales, e incluso la propia clase (con lo que todos sus métodos públicos lo serán) pero como veremos más adelante eso nos puede dar algún problema por lo que hay que ser cautos.


    Ámbito de la transacción

    Una cosa que se ha de tener clara es cuanto dura una transacción. Esto es, cuando empieza y cuando termina. Si tenemos anotado un método con @Transactional, la transacción empezará justo antes de la primera línea del método y terminará justo después de la última. Si dentro de esto método existen llamadas a otros métodos estos otros métodos son llamados dentro de la transacción sin necesidad de anotarlos con @Transactional.
    Ahora bien ¿qué sucede cuando un método anotado con @Transactional llama a otro método anotado también con @Transactional?

    public class A {
     
        @Autowired
        private B b;
     
        @Transactional
        public void hazAlgoA() {
            b.hazAlgoB();
        }
     
    }
     
    public class B {
     
        @Transactional
        public void hazAlgoB() {
            hazAlgoTransaccionalmente();
        }
    }
    

    Se mantiene la misma transacción? ¿Se abre una nueva?… 
    Bueno, a esto se le llama propagación de la transacción. 

    Por defecto una transacción tiene una propagación de PROPAGATION_REQUIRED, si la transacción no existe la crea y si la tiene la aprovecha. Según esto, el primer método abre la transacción y el segundo la aprovecha por lo que todo está dentro de la misma transacción.
    Ahora supongamos que queremos justo lo contrario. Necesitamos que el método de la claseB utilice una nueva transacción, de esta manera si algo falla en B, la transacción de A no hace rollback. Tal como está actualmente no nos sirve por lo que hemos de modificar la propagación de B de la manera siguiente.
    @Transactional( propagation = Propagation.REQUIRES_NEW )
    public void hazAlgoB() {
        hazAlgoTransaccionalmente();
    }
    
    Ahora, al entrar en B, sí se generará una nueva transacción.
    Spring soporta las siguientes propagaciones:
    • PROPAGATION_REQUIRED - Es la que viene por defecto, así que no es necesaria especificarla. Si existe transacción la aprovecha y sino la crea
    • REQUIRES_NEW - Abre una transacción nueva y pone en suspenso la anterior. Una vez el método marcado como REQUIRES_NEW termina se vuelve a la transacción anterior.
    • PROPAGATION_SUPPORTS - Si existe transacción la aprovecha, sino no crea ninguna.
    • PROPAGATION_MANDATORY - Si no existe una transacción abierta se lanza una excepción. Hay gente que anota sus DAO con esta opción.
    • PROPAGATION_NEVER - Si existe una transacción abierta se lanza una excepción. No se me ocurre ningún ejemplo donde esto sea necesario pero seguro que alguno hay.
    • PROPAGATION_NOT_SUPPORTED - Si existe una transacción la pone en suspenso, la transacción se reactiva al salir del método.

    Commit y Rollback

     Ahora lo que queremos es controlar el commit o elrollback de nuestra transacción. La cosa es simple, si un método anotado como@Transaccional lanza una excepción que herede de RuntimeException se producirá un rollback. En caso contrario, un commit.
    Ok, vamos a dar vueltas a esto. Primero lo más evidente:
    public class A {
     
        @Autowired
        private B b;
     
        public void hazAlgoA() {
            try {
                b.hazAlgoB();
            }
            catch( NullPointerExecption e) {
            }
        }
     
    }
     
    public class B {
     
        @Transactional
        public void hazAlgo(Entidad entidad) {
            // inserto un dato en bbdd
     
            throw new NullPointerException();
     
            // inserto otro dato en bbdd
        }
    }
    
    lanza una NullPointerException, que hereda de RuntimeException, luego se produce un rollback y la primera inserción no se realiza (ni la segunda, claro).
    Veamos esto
    public class A {
     
        @Autowired
        private B b;
     
        public void hazAlgoA() {
            try {
                b.hazAlgoB();
            }
            catch( FileNotFoundException e) {
            }
        }
     
    }
     
    public class B {
     
        @Transactional
        public void hazAlgo(Entidad entidad) {
            // inserto un dato en bbdd
     
            throw new FileNotFoundException();
     
            // inserto otro dato en bbdd
        }
    }
    
    ¿Qué hará? ¿Inserta un dato? ¿Dos? ¿Ninguno? Veamos, FileNotFoundException no hereda de RuntimeException por lo que no debería hacer rollback, pero lanzamos la excepción antes de insertar el segundo dato así que el flujo del programa no llegaría al código de insertarlo, pero si hace el primero. Podemos imaginar que hay algo así:


    public void hazAlgo(Entidad entidad) {
        try{
            tx.begin
     
            // inserto un dato en bbdd
            throw new FileNotFoundException();
     
            // inserto otro dato en bbdd
        }
        catch(Exception e ) {
            if( e instanceof RuntimeException ) {
                tx.rollback();
                tx.close();
            }
            else {
                tx.commit();
                tx.close();
            }
            throw e;
        }
    }
    
    ¡Esto no es el código que hay realmente! Tan sólo es poner en pseudocódigo lo explicado anteriormente.
    Compliquemoslo un poco más:
    public class A {
     
        @Autowired
        private B b;
     
        public void hazAlgoA() {
            b.hazAlgoB();
        }
     
    }
     
    class B {
     
        @Autowired
        private C c;
     
        @Transactional
        public void hazAlgoB() {
            // inserto un dato en bbdd
            try {
                c.hazAlgoC();
            catch( NullPointerExecption e) {
            }
            // inserto otro dato en bbdd
     
        }
    }
     
    class C {
     
        @Transactional
        public void hazAlgoC() {
            // inserto un dato en bbdd
     
            throw new NullPointerException();
     
            // inserto otro dato en bbdd
        }
    }
    
    Esta es más interesante. La clase C lanza una NullPointerException que hereda unaRuntimeException ergo debería hacer un rollback. Pero, pero, pero, la clase B, donde se inicia la transacción ya tenía previsto este error, así que captura la excepción y no la vuelve a lanzar. ¿Hará un rollback? ¿Hará un commit? Hagan sus apuestas.
    Bueno, hace un rollback. El punto es que si una RuntimeExcepcition sale de un método anotado como @Transactional, toda la transacción queda marcada como rollback. No importa que no fuese el método desde el que se inició la transacción, si su propagación es normal (esto es, no es una REQUIRES_NEW), en cuanto lanzamos la excepción estamos vendidos.
    Esto es importante por un motivo, hay gente que, ante la duda, anota todos los métodos o las clases con @Transactional pero esto supone un problema de concepto de construcción. El encargado de saber si la transacción ha de marcarse como rollback ocommit es siempre el método desde donde se inicia la transacción, nunca uno de los métodos a los que llama. Si yo llamo a un método externo que está marcado como transaccional y este por algún motivo me lanza una RuntimeExcepción, automáticamente me obliga a hacer un rollback. No me deja la opción de que sea yo el que valore si puedo continuar o no, quizás ya tenía previsto que podía saltar esa excepción y la capturaba para tratarla. Lamentablemente, al utilizar una librería que anotó su método con@Transactional me limita de cualquier intento de recuperarme de la excepción.
    Moraleja, sólo hay que poner @Transactional en métodos donde se abre, cierra y controla la transacción. En el resto de métodos su colocación puede ser contraproducente.

    Controlar las excepciones que hagan commit o rollback

    El hecho anterior, que una transacción haga rollback si se lanza una RuntimeExcecption y commit si no, es algo demasiado rígido para muchos casos. Hay momentos que no queremos que se haga un rollback para alguna excepción en concreto o lo contrario, no queremos que haga un commit cuando se lanza una excepción que no herede de RuntimeException. Para esto tenemos las propiedades noRollbackFor yrollbackFor, con estas opciones podemos configurar el commit y el rollback según nuestras necesidades.
    Por ejemplo:
    @Transactional(noRollbackFor={NumberFormatException.class,ArithmeticException.class})
    public void hazAlgoTransaccionalmente() {
        // Soy transaccional!
    }
    
    Hará un commit incluso si lanza las excepciones NumberFormatException oArithmeticException o excepciones que hereden de estas. Lo de las excepciones que hereden de esta es muy importante para tener en cuenta su alcance. Hay gente que lo aprovecha para hacer cosas del tipo:


    @Transactional(noRollbackFor={RuntimeException.class})
    public void hazAlgoTransaccionalmente() {
        // Soy transaccional!
    }
    
    lo cual es una muy pésima idea porque implica que el método nunca hará un rollback.
    Tal como he dicho existe la propiedad inversa, rollbackFor. Con esta podremos conseguir que la transacción haga un rollback para las excepciones que no hereden deRuntimeException.
    Por ejemplo
    @Transactional(rollbackFor={FileNotFoundException.class})
    public void hazAlgoTransaccionalmente() {
        // Soy transaccional!
    }
    
    hará un rollback si lanza una FileNotFoundException a pesar de que esta no hereda de RuntimeException.

    Transacciones Declarativas 

    Permite gestionar las transacción por medio de spring bean configuracion o archivo de configuración en lugar de la codificación dura en su código fuente o la utilización de notaciones.

    Se realizara de la siguiente forma:
    • Debemos tener definido el medio de datos con el que vamos a trabajar en nuestra aplicación  es decir la definición del datasource.
    • Primero vamos a definir la utilización de administrador de transacciones suficiente y necesario para nuestra forma de acceso a base de datos, en el ejemplo hace referencia a un administrador asociado al trabajo JDBC, pero podría ser cualquiera de los ya mencionados en este articulo, pero tiene otra particularidad la inyección del medio de datos o datasource que proporciona la interacción con la base de dato.
    • El siguiente paso sera definir advise transaccional que hace referencia en primera instancia al administrador de transacciones y se coloca los patrones a laos que se aplicara en nuestro caso hago referencia directamente al nombre de un metodo "mimetodo", pero por ejemplo podría utilizar <tx:method name="mimetodo2*" y estaria definiendo un patron en el cual aceptaria todo los metodos que comiencen con mimetodo2, ejemplo mimetodo2alta(), mimetodo2baja()..etc.
    • El siguiente paso es generar la configuración del aspecto  transaccional,  quien hace referencia a un pointcut que no es otra cosa que la condicion bajo la que se ejecutaran los advise,  y defino el advisor que hace referencia al advise y al pointcut.

    Nota: Ahora soy yo o me parece que tengo 2 puntos de filtro definidos, por un lado el patron del advise propiamente dicho y por otro lado el pointcut en la configuracion del aspecto. Si es verdad la diferencia es simple el pointcut es un filtro con muchas posibilidades que define el ámbito de control sobre donde se realizara, es decir una clase, un paquete para que luego el patron del advice pueda trabajar con su condición sobre el ámbito.

    Nota Final: Si el método finaliza normalmente, el advise  AOP confirma la transacción  con éxito (commits),  de lo contrario  lleva a cabo una operación de deshacer (rollback).


    domingo, 6 de octubre de 2013

    Spring:Como Mejorar JDBC

    Recorriendo Spring
    Creo si bien es una forma limitada de tratamiento de los datos de una aplicación, es la base de administración de conexiones a motores de base de datos con la que cuenta java.

    El objetivo es proporcionar un pantallaso de como se hacia JDBC con la api nativa y como puede hacer JDBC con el soporte SPRING.

    Si programaste en java de seguro le diste una pasada a JDBC por que no creo que hayas amado hibernate todo tu vida perdiendo percepción de otras formas de tratamiento de datos.

    Pero la primera pregunta seria que es JDBC ?

    JAVA DATABASE CONNECTIVITY es una API de java para conectar aplicaciones a base de datos, cuenta con una arquitectura modular, una misma interfaz para diferentes motores de base de datos implementando un gestor de drive para cada motor.

    Que quiere decir que cada motor proporciona un drive que posibilita que JDBC realice la magia de la conectividad.



    Pero para toda esta estructura de comunicación utiliza un paquete llamado java.sql

    Los controladores de las BD son:

    • Clase DriverManager:  Permite establecer y gestionar conexiones a las BD
    • Clase SQLPermission: Proporciona los permisos para poder usar el DriverManager a código en ejecución dentro de un Security Manager (por ejemplo applets) 
    • Interfaz Driver:  Métodos para registrar y conectar controladores basados en tecnología JDBC 
    • Clase DriverPropertyInfo: Propiedades de un controlador
    • Excepciones: dispone de SQLException y SQLWarning


    Interfaz con la aplicación: es claro que nuestra aplicación tendrá como responsabilidad el uso de sentencias que posibilitan el acceso al modelo de datos.

    Connection:  Métodos para crear instrucciones y para gestionar conexiones y sus propiedades
    Statement:  Permite enviar instrucciones a la BD
    PreparedStatement:  Permite usar instrucciones preparadas o SQL básicas
    CallableStatement:  Llamada a procedimientos almacenados en la BD
    Savepoint:  Puntos de recuperación en una transacción 

     Recuperación de los resultados de la consulta a la BD
    ResultSet: Conjunto de resultados que se devuelven de una query
    ResultSetMetaData:  Información sobre las columnas del objeto ResultSe

    En cuanto a lo que se refiere al como trabajar  por lo general se sigue un flujo de trabajo como el siguiente.

    •  Establecer la conexión con la BD
    • Cargar controladores (si se usa una versión de Java inferior a la 6)
    • Establecer la conexión
    • Crear un objeto Statement para hacer petición a la BD
    • Asociar una sentencia SQL al objeto Statement
    • Proporcionar valores de los parámetros
    • Ejecutar el objeto Statement
    • Procesar los resultados
    • Liberar recursos (cerrar la conexión)

    Nota: Si es necesario, se pueden ejecutar varias instrucciones dentro de una transacción (propiedades ACID)

    • Abrir transacción
    • Crear y ejecutar instrucciones
    • Procesar resultados
    • Cerrar transacción

    Pero la realidad es que esto siempre fue mas facil de observar si lo miramos a nivel código  por ejemplo visualicemos una clase CustomerDaoImpl que tendria la responsabilidad de realizar las operaciones de la base de datos.

    Por supuesto la clase recibirá el datasource de una capa superior 

    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
    }

    El metodo insertar recibirá un objeto de tipo customer para que el método pueda procesarlo para la operación con la base de datos, en su interior lo que por lo general realizaríamos en primera instancia un amplo manejo de excepciones  para la operación con JDBC y por otro lado realizaríamos un procesamiento normal,  obtener la conexión con la base de datos, preparar el el comando que dispararemos , setear los parametros del query que vamos a enviar, ejecutaremos el comando y gestionaremos el cierre de la conexión intentando que no quede colgada ninguna conexión.

    public void insert(Customer customer){

    String sql = "INSERT INTO CUSTOMER " +
    "(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
    Connection conn = null;

    try {
    conn = dataSource.getConnection();
    PreparedStatement ps = conn.prepareStatement(sql);
    ps.setInt(1, customer.getCustId());
    ps.setString(2, customer.getName());
    ps.setInt(3, customer.getAge());
    ps.executeUpdate();
    ps.close();

    } catch (SQLException e) {
    throw new RuntimeException(e);

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


    Nota: en los viejos tiempos muchas veces el low performance de una aplicacion desarrollada en JDBC,  estaba en que no estaba gestionada las aperturas y cierres de conexiones en la interacción con la base de datos, donde teníamos un programador feliz de haber terminado un desarrollo y de pronto 1 semana después te decía el analista, el cliente dice que funciona muy lenta la aplicación  entonces comenzabas a monitorear el servidor y tenias conexiones colgadas por todos lados, conexiones que nunca se cerraban.  

    Como nota final había un manejo de excepciones muy grande que se realizaba sobre toda una capa de negocio desarrollada con jdbc a fin de prevenir problemas de interacción y las conexiones, tareas repetitivas y tediosas.

    Ahora para los que ya trabajaron bajo el modelo tradicional de trabajo con JDBC conoce un poco sobre las limitaciones que ofrecía el esquema de trabajo:


    • Se debe escribir demasiado código para cosas simples como operaciones CRUD.
    • Mucho del código a escribir es repetitivo lo que puede llevar a errores o inconsistencias.
    • Se debe manejar la creación de las conexiones, la preparación de las consultas su ejecución y el cierre de las conexiones.
    • Se debe manejar las excepciones dado que no todas pueden ser capturadas en JDBC, por lo que requiere mucho control un trabajo bien realizado.

    Como todo el mundo una alternativa es migrar por supuesto a un esquema basado en ORM que nos facilite el trabajo y elimine tareas repetitivas, pero de seguro hay gente que puede pensar que para una implementacion de este tipo, el desarrollo debe justificarlo por lo que es logico que un programador en la actualidad se encuentra mantenimientos que trabajan con JDBC puro, por lo que es una alternativa de mejorar las prestaciones del codigo sin emprender una migración de hitos tecnológicos que pueda tener muchas horas de desarrollo, por que spring es fácil adaptable a proyectos. 

    El valor agregado proporcionado por la abstracción de Spring Framework JDBC es quizás la mejor manera de mostrar la secuencia de las acciones indicadas en la tabla de abajo. La tabla muestra las acciones que Spring se hará cargo y las acciones que son responsabilidad suya, el desarrollador de la aplicación.


    Acción
    Spring
    Nosotros
    Definir los parámetros de conexión.

    X
    Abrir la conexión.
    X

    Especificar la sentencia SQL.

    X
    Declarar parámetros y proporcionar valores de los parámetros.

    X
    Elaborar y ejecutar la sentencia.
    X

    Configurar el bucle para iterar a través de los resultados (si los hay).
    X

    Hacer el trabajo para cada iteración.

    X
    Procesar cualquier excepción.
    X

    Manejar las transacciones.
    X

    Cerrar connection, statement y resultset.
    X


    Usted puede elegir entre varios enfoques para acceder a  la base de datos JDBC. 

    Además de los tres  JdbcTemplate, un nuevo enfoque SimpleJdbcInsert y SimplejdbcCall optimizan los metadatos de la base de datos. 

    • JdbcTemplate es el clásico enfoque de Spring JDBC y el más popular. Este enfoque de "nivel más bajo" es el que todos los otros utilizan a nivel interno.
    •  NamedParameterJdbcTemplate envuelve un JdbcTemplate para proporcionar los nombres de los parámetros en lugar del tradicional marcador de posición "?" de JDBC.
    • SimpleJdbcTemplate combina las operaciones de uso más frecuente de dbcTemplate y NamedParameterJdbcTemplate.
    • SimpleJdbcInsert y SimpleJdbcCall optimizan el metadato de la base de datos para limitar la cantidad de configuración necesaria. Este enfoque simplifica la codificación de manera que usted sólo necesita proporcionar el nombre de la tabla o un procedimiento y proporcionar un mapa de parámetros que coincidan con los nombres de las columnas. Esto sólo funciona si la base de datos proporciona los metadatos adecuados. Si la base de datos no proporciona estos metadatos, usted tendrá que proporcionar la configuración explícita de los parámetros.

    La abstracción del framework de Spring Framework JDBC consiste en cuatro paquetes diferentes,  core, datasource, object, y support.


    • El paquete org.springframework.jdbc.core contiene la clase JdbcTemplate y sus diferentes interfaces de retro llamada, además de una variedad de clases relacionadas. Un subpaquete llamado org.springframework.jdbc.core.simple contiene la clase SimpleJdbcTemplate y las clases relacionadas SimpleJdbcInsert y SimpleJdbcCall. Otro subpaquete llamado org.springframework.jdbc.core.namedparam contiene la clase NamedParameterJdbcTemplate y las clases relacionadas de apoyo.
    • El paquete org.springframework.jdbc.datasource contiene una clase de utilidad para el fácil acceso a DataSource, y varias implementaciones simples de DataSource que pueden ser utilizadas para probar y ejecutar sin modificar el código JDBC fuera de un contenedor Java EE. Un subpaquete llamado org.springfamework.jdbc.datasource.embedded proporciona soporte para crear instancias de base de datos en memoria utilizando los motores de bases de datos Java, tales como HSQL y H2.
    • El paquete org.springframework.jdbc.support proporciona una funcionalidad de traducción SQLException y algunas clases de utilidad. Las excepciones que se producen durante el procesamiento JDBC son traducidas a excepciones definidas en el paquete org.springframework.dao. Esto significa que utilizando el código, la capa de abstracción de Spring JDBC no necesita implementar un tratamiento de errores de JDBC o un RDBMS específico.
    La clase JdbcTemplate es la clase central en el paquete principal de JDBC. Esta maneja la creación y liberación de recursos, que le ayuda a evitar errores comunes, como olvidar cerrar la conexión. Esta realiza las tareas básicas del flujo de trabajo principal de JDBC, como la creación de declaración y ejecución, dejando el código de la aplicación para proporcionar SQL y extraer resultados. 

    La clase JdbcTemplate ejecuta consultas SQL, sentencias de actualización y llamadas a procedimientos almacenados, realiza iteraciones sobre ResultSets y la extracción de valores de los parámetros devueltos. También captura las excepciones JDBC y las traduce a genéricas más informativas, que se define en el paquete org.springframework.dao.

    En primera instancia como buena practica utilizaremos el patron DAO para nuestros ejemplos.

    "Data Access Object" un patrón de diseño muy común que se usa para diseñar como los objetos de negocio de una aplicación deben usar los objetos encargados del acceso a los datos.



    Como pueden ver los objetos del negocio acceden a los DAO a través de sus interfaces, de esta manera no dependen de una implementación en específico lo que hace que esta implementación se pueda cambiar fácilmente. También facilita las pruebas que se pueden realizar sobre esta implementación, ya que se pueden crear implementaciones falsas menos complejas que las implementaciones reales con fines de prueba.

    Para nuestro ejemplo vamos a trabajar como siempre con un proyecto creado con Maven para que nos solucione las dependencias.


    Por otro lado abordaremos la utilización de JdbcTemplate en nuestro proyecto dado que es la implementacion nativa que utilizan las diferentes formas que proporciona spring para trabajar con JDBC.


    En esta oportunidad utilizaremos MYSQL como motor de base de datos en donde vamos a crear una base de datos llamada estudiantes y posterior crearemos una tabla estudiantes con la siguiente estructura.

    CREATE TABLE Estudiante(
       ID   INT NOT NULL AUTO_INCREMENT,
       NOMBRE VARCHAR(20) NOT NULL,
       EDAD  INT NOT NULL,
       PRIMARY KEY (ID)

    );

    como siempre el objetivo es con fines educativos por lo que no desarrollaremos grandes códigos pero si aplicaremos buenas practicas que vayan formando nuestra forma de trabajar.

    La estructura de nuestro proyecto es la siguiente 























    Pero es la hora de recorrer nuestro proyecto a fin de poder hablar de las virtudes de utilizacion de spring para mejorar JDBC.

    Pom.xml: Nuestro archivo de configuración de dependencias  para el proyecto.


    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.company</groupId>
      <artifactId>jdbcspring01</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <properties>
       <spring.version>3.0.5.RELEASE</spring.version>
      </properties>
      <dependencies>
       <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
       </dependency>
       <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
       </dependency>
       <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
       </dependency>
       <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.26</version>
       </dependency>
       <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
       </dependency>
      </dependencies>
    </project>
    

    Nota: no va presentar grandes inclusiones  por un lado las típicas referencias de trabajo de spring y por supuesto el conector asociado a MYSQL.

    com.company.modelo.Estudiante.java: Nuestro pojo que por la simplicidad del ejemplo solo tendra los campos asociados a la tabla con la que trabajaremos.


    package com.company.modelo;
    
    public class Estudiante {
    
        private Integer edad;
        private String nombre;
        private Integer id;
        
     public Integer getEdad() {
      return edad;
     }
     public void setEdad(Integer edad) {
      this.edad = edad;
     }
     public String getNombre() {
      return nombre;
     }
     public void setNombre(String nombre) {
      this.nombre = nombre;
     }
     public Integer getId() {
      return id;
     }
     public void setId(Integer id) {
      this.id = id;
     }  
        
    }
    
    

    Nota: Podemos observar que solo tiene 3 campos ID, NOMBRE, EDAD que son casualmente los campos de nuestra tabla estudiantes en MYSQL.

    com.company.dao.EstudianteDao.java: Definimos el contrato de los métodos que deberán incluir las clases que implementen esta interfaz. 


    package com.company.dao;
    
    import java.util.List;
    
    import javax.sql.DataSource;
    
    import com.company.modelo.Estudiante;
    
    public interface EstudianteDao {
        /*
         * Metodo utilizado para inicializar la conexion con la bd
         */
     public void setDataSource(DataSource ds);
     public void Agregar(String nombre, Integer edad);
     public Estudiante getEstudiante(Integer id);
     public List listarEstudiantes();
     public void Eliminar(Integer id);
     public void Actualizar(Integer id, Integer edad);
    }
    
    

    Nota: Como particularidad debemos recalcar el método setdatasource que es por donde podremos suministrar el datasource de conexión con la base de datos y luego la definición de los métodos asociados a un CRUD.

    com.company.dao.EstudianteJDBCTemplate.java: Es la implementacion dao, que logicamente implementa el codigo para las operaciones CRUD sobre la tabla estudiante. 

    package com.company.dao;
    
    import java.util.List;
    
    import javax.sql.DataSource;
    
    import org.springframework.jdbc.core.JdbcTemplate;
    
    import com.company.modelo.Estudiante;
    
    public class EstudianteJDBCTemplate implements EstudianteDao {
      private DataSource dataSource;
      private JdbcTemplate jdbcTemplateObject;   
     
      public void setDataSource(DataSource dataSource) {
       this.dataSource = dataSource;
       this.jdbcTemplateObject = new JdbcTemplate(dataSource);
      }
    
     public void Agregar(String nombre, Integer edad) {
        String SQL = "insert into estudiante (nombre, edad) values (?, ?)";       
           jdbcTemplateObject.update( SQL, nombre,edad);
           System.out.println("Registro creado nombre = " + nombre + " edad = " + edad);
           return;
     }
    
     public Estudiante getEstudiante(Integer id) {
      String SQL = "select * from estudiante where id = ?";
         Estudiante student = jdbcTemplateObject.queryForObject(SQL, 
                             new Object[]{id}, new EstudianteMapper());
         return student;
     }
    
     public List listarEstudiantes() {
      String SQL = "select * from estudiante";
           List  students = jdbcTemplateObject.query(SQL, 
                                     new EstudianteMapper());
          return students;
     }
    
     public void Eliminar(Integer id) {
       String SQL = "delete from estudiante where id = ?";
           jdbcTemplateObject.update(SQL, id);
           System.out.println("Registro eliminado ID = " + id );
           return;
     }
    
     public void Actualizar(Integer id, Integer edad) {
        String SQL = "update estudiante set edad = ? where id = ?";
           jdbcTemplateObject.update(SQL, edad, id);
           System.out.println("registro actualizado  ID = " + id );
           return;
    
     }
     
    
    }
    

    El código de la implementacion es la que nos permite indagar sobre las ventajas de spring sobre el manejo de JDBC.

    Primero lo que debemos recalcar es la utilizacion de 2 variables privadas 


    La primera hace referencia al datasource de la base de datos que sera sostenida en la tabla
    private DataSource dataSource;
    La segunda hace referencia a la template para trabajar con JDBC que nos proporciona spring.

    private JdbcTemplate jdbcTemplateObject;   

    El primer metodo que vamos a encontrar es el setDataSource que como parametro de ingreso recibe un datasource que lógicamente sera suministrado por inyección dado que este objeto esta precargado en el spring bean configuration que veremos mas adelante. En su interior el metodo asigna el valor a la variable privada para este fin, pero por otro lado genera una instancia de la template que nos proporciona spring pasandole la conexion con la base de datos para se encargue de ahora en mas.

    Los métodos que realizan operaciones de actualización en la clase Agregar, Eliminar y Actualizar tienen una misma lógica de funcionamiento.

    Primero definimos la cadena con la sentencia SQL indicando con ? la necesidad de un parámetro externo, ejemplo 

    insert into estudiante (nombre, edad) values (?, ?)
    delete from estudiante where id = ?
    update estudiante set edad = ? where id = ?

    lo loco es que spring con solo hacer referencia al metodo update de la template , pasandole la query y los parametros que tiene que remplazar en la misma se encarga de todo.

    sisi!!

    yo pongo jdbcTemplateObject.update(query.., parametro1, parametro 2 .....parametro n); con esto spring se encarga de todo, es decir que si le paso una sentencia de insert va realizar un alta, si le paso una update realizara una actualizacion y si le paso una sentencia delete realizara una eliminacion.

    ahora lo que llega es analizar los metodos de buscar un registro en particular el metodo getEstudiante y listarEstudiantes() donde la unica diferencia es que el primero retorna solo un objeto y el segundo retorna una lista de objetos, pero en su codigo no presentan casi ninguna diferencia dado que ambas realizan un procesamiento similar.

    Para la busqueda de un registro utiliza el metodo queryForObject a quien le pasa la sentencia de consulta SQL, le pasa la generación de la instancia para el objeto de retorno  y genera una instancia de la clase Mapper quien recibirá el resultado de la consulta y generara un objeto para cada fila del resulset que reciba que para este caso sera solo 1. 

    String SQL = "select * from estudiante where id = ?";
    Estudiante student = jdbcTemplateObject.queryForObject(SQL, 
                           new Object[]{id}, new EstudianteMapper());

    Para la búsqueda de varios registros utiliza el método query a quien le pasa la sentencia SQL y una nueva instancia del objeto mapper que en este caso retorna una lista de objetos generada como producto de la generación de un objeto por cada fila del resulset resultante.


    String SQL = "select * from estudiante";
    List students = jdbcTemplateObject.query(SQL, 
                                   new EstudianteMapper());

    A mi modo de ver las cosas spring no hace otra cosa que lo que nosotros hacíamos antes de manera manual, por un lado generábamos una clase base que contenga método para operaciones de actualización y consulta y luego utilizábamos en las implementaciones la llamada a estos métodos para evitar código repetitivo y estos métodos ya tenían una gestión de excepciones propia ...etc la diferencia que ahora estas clases manuales te las brinda spring con la diferencia que están mucho mas cuidadas que quizás las que programábamos nosotros o quizás no, dado que la nuestra era propia acuerdo a nuestras necesidades. En el pasado hubiera significado que los programadores no tuviéramos que haber programado clases DAL propias para esta gestión.

    com.company.dao.EstudianteMapper.java:  Realiza el mapeo de cada fila de un resulset en un objeto del tipo estudiante identificado.

    package com.company.dao;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    import org.springframework.jdbc.core.RowMapper;
    
    import com.company.modelo.Estudiante;
    
    public class EstudianteMapper implements RowMapper {
    
     public Estudiante mapRow(ResultSet rs, int arg1) throws SQLException {
      Estudiante student = new Estudiante();
           student.setId(rs.getInt("id"));
           student.setNombre(rs.getString("nombre"));
           student.setEdad(rs.getInt("edad"));       
      return student;
     }
    
     
    }
    
    

    Nota: Recordemos que cuando usábamos un JDBC normal, después de hacer un executeQuery(), haciamos un while (rs.next()) y dentro del bucle haciamos un new a cada objeto por  cada fila y lo agregábamos a una lista. Pues con el Spring es lo mismo, pero con la diferencia que con el resulset para cada fila genera un objeto en lista. 

    src/main/resource/springjdbc.xml:  Nuestro archivo de configuración de bean del proyecto.


    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
      <bean id="dataSource" 
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
          <property name="url" value="jdbc:mysql://localhost:3306/estudiante"/>
          <property name="username" value="root"/>
          <property name="password" value=""/>
       </bean>
       
       <bean id="EstudianteJDBCTemplate" 
          class="com.company.dao.EstudianteJDBCTemplate">
          <property name="dataSource"  ref="dataSource" />    
       </bean>
       
    </beans>
    

    Nota: El archivo no presenta grandes novedades en primera instancia la declaración de nuestro dataource y la inyeccion del mismo en la clase EstudianteJDBCTemplate.

    com.company.test.EstudianteTest.java: Nuestra clase de test del proyecto que es donde podremos ver el funcionamiento de las clases antes mencionadas.

    package com.company.test;
    
    import java.util.List;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.company.dao.EstudianteJDBCTemplate;
    import com.company.modelo.Estudiante;
    
    public class EstudianteTest {
    
     public static void main(String[] args) {
      ApplicationContext context = 
                  new ClassPathXmlApplicationContext("springjdbc.xml");
    
           EstudianteJDBCTemplate studentJDBCTemplate =(EstudianteJDBCTemplate)context.getBean("EstudianteJDBCTemplate");
           
           System.out.println("------Alta de registros--------" );
           studentJDBCTemplate.Agregar("Zara", 11);
           studentJDBCTemplate.Agregar("Nuha", 2);
           studentJDBCTemplate.Agregar("Ayan", 15);
    
           System.out.println("------Listado de registros--------" );
           List students = studentJDBCTemplate.listarEstudiantes();
           for (Estudiante record : students) {
              System.out.print("ID : " + record.getId() );
              System.out.print(", Nombre : " + record.getNombre() );
              System.out.println(", Edad : " + record.getEdad() );
           }
    
           System.out.println("----Actualizando ID = 2 -----" );
           studentJDBCTemplate.Actualizar(2, 20);
    
           System.out.println("----Listando registro ID = 2 -----" );
           Estudiante student = studentJDBCTemplate.getEstudiante(2);
           System.out.print("ID : " + student.getId() );
           System.out.print(", Nombre : " + student.getNombre() );
           System.out.println(", Edad : " + student.getEdad() );
    
     }
    
    }
    
    

    Nota: Donde no hacemos nada raro de la habitual, pero genero el contexto apartir del spring bean configuración, segundo obtengo el bean EstudianteJDBCTemplate con el que ejecutaremos algunos de los metodos implementados en la clase para verificar su funcionamiento.

    Analizando el resultado de ejecución:



    El código del proyecto se lo puede descargar desde aquí. codigofuente.zip 

    Nota Final: Reduce gran cantidad de código repetido que tenia JDBC, me quita la responsabilidad de gestionar las excepciones por que lo realiza spring, me quita la responsabilidad de controlar las conexiones con la base de datos, me colabora con la generación de listas de objetos permitiendo un mapeo parcial con el contenido de la base de datos.

    Listo me solucionaste todos los problemas por lo que migre a utilizar formas de acceso a la base de datos mas potentes HIBERNATE,.... YA FUE ME DECIDÍ LARGO TODO ESTO Y COMIENZO A DESARROLLAR CON ESTO!!!  .mmm no hay chance JAJA si es verdad mejoro mucho el trabajo con JDBC pero no tiene comparacion en la facilidad que nos brindan estas grandes herramientas de trabajo, pero si es bueno tenerlo en cuenta cuando nos topamos con algún proyecto viejo que podríamos optimizar para no migrarlo o algun desarrollo pequeño para lo que HIBERNATE le quede grande.

    Conociendo un poco de JdbcDaoSupport

    Un plus seria Extends JdbcDaoSupport a nuestra clase de implementacion dao, dando basicamente le quitamos la responsabilidad a JdbcTemplate de gestionar la obtencion del datasource, dado que con solo inyectar el datasource a la clase de implementacion dao, jdbcDaoSupport se encargaria de manera automatica de la gestion del medio de dato.

    Es decir este método en la clase de implementacion  ya no seria necesario 

     public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
    this.jdbcTemplateObject = new JdbcTemplate(dataSource);


    }

    Nota: Por lo que seguimos simplificando el código con el que trabajamos.

    Conociendo callback RowCallbackHandler

    ¿Y si no hubiese que devolver ningún resultado? Por ejemplo, procesar una a una las filas del resultado y generar un informe con ellas. Para ello, Spring proporciona el callback rowCallbackHandler.

    Cuando realizábamos una consulta por ejemplo utilizabamos el mapper para trabajar en el retorno, dado que en este caso retorno un objeto, pero si no retornara nada.?? 


    String SQL = "select * from estudiante where id = ?";
    Estudiante student = jdbcTemplateObject.queryForObject(SQL, 
                            new Object[]{id}, new EstudianteMapper());

    y una caso donde no retorno nada seria como lo siguiente.

     jdbcTemplate.query("SELECT * FROM estudiante", new EstudianteRowCallbackHandler());

    donde la clase seria una definición como la siguiente.

    class EstudianteRowCallbackHandler implements RowCallbackHandler { public void processRow(ResultSet rs) throws SQLException { System.out.println("--Estudiante--"); System.out.println("Nombre: " + rs.getString(1)); System.out.println("edad: " + rs.getString(2)); } }

    y en la ejecución seria como si recorriéramos el resulset por consola y a veces utilizado cuando queremos ver que esta devolviendo la consulta sin que agreguemos ningún código para hacerlo.