Haciendo nuestros Data Access Object (DAO) transparentemente transaccionales

Y lo prometido es deuda, aquí­ viene un pequeño artí­culo con una idea para hacer las llamadas a vuestros DAOs transaccionales de forma totalmente transparente.
Hasta ahora por lo que habí­amos visto éramos capaces conociendo y aplicando los patrones DAO+DTO de encapsular nuestras llamadas al modelo de negocio, ordenarlas cada una en su DAO correspondiente, garantizar una íºnica instancia de cada uno de los DAO utilizando un Singleton… y algunas cosillas más.
Pero algo que nos quedaba pendiente resolver era cómo hacer que las llamadas a nuestros DAO se llevaran a cabo de forma transaccional. Es decir, que en caso de que en varias llamadas consecutivas a nuestros DAO (llamdas que alteren el contenido de una entidad de destino), podamos volver al estado anterior en caso de que ocurra un error en la aplicación.
Me explico. Por lo que conocí­amos por ahora podí­amos hacer lo siguiente.
try
{
 // insertamos tres coches
 CocheDAO.GetInstance().Insert(codigocoche1, “seat”);
 CocheDAO.GetInstance().Insert(codigocoche2, “mercedes”);
 CocheDAO.GetInstance().Insert(codigocoche3, “renault”);
 // actualizamos el coche 2
 CocheDAO.GetInstance().Update(codigocoche2, “honda”);
 // borramos el coche 1
 CocheDAO.GetInstance().Delete(codigocoche1);
}
catch (Exception ex)
{
 TratarExcepcion(ex);
}
Y qué ocurre si al insertar el coche níºmero 3 la aplicación falla. Pues que se habrán creado dos coches que quedarán colgados en la base de datos. ¿Cómo solucionamos esto? Creando una transacción. Pero claro, entonces aquí­ viene el gran problema… ¿de dónde narices saco la transacción si no tengo acceso a la conexión de base de datos? Este es el chiste del DAO, que no veo la conexión, es totalmente transparente a mi… entonces… ¿cómo voy a hacer algo transaccional?
Entonces es cuando aparece la idea feliz y nos inventamos unas transacciones propias para los DAO. Algo así­ como un TransactionDAO. Entonces nuestro código anterior cambia un poco y pasa a ser el siguiente:
try
{
 TransactionDAO tran = BaseDAO.BeginTransaction();
 // insertamos tres coches
 CocheDAO.GetInstance().Insert(tran, codigocoche1, “seat”);
 CocheDAO.GetInstance().Insert(tran, codigocoche2, “mercedes”);
 CocheDAO.GetInstance().Insert(tran, codigocoche3, “renault”);
 // actualizamos el coche 2
 CocheDAO.GetInstance().Update(tran, codigocoche2, “honda”);
 // borramos el coche 1
 CocheDAO.GetInstance().Delete(tran, codigocoche1);
 tran.Commit();
}
catch (Exception ex)
{
 tran.Rollback();
 TratarExcepcion(ex);
}
Parece esto una llamada a transaccional tí­pica, ¿no? Pero hay una sustancial diferencia, es que la transacción no es una transacción de base de datos que nos pueda proveer por ejemplo .NET (SqlTransaction) si no que es una transacción propia del DAO y que nos provee para hacer las llamadas transaccionales. El cómo implemente internamente la transacción el DAO ya es problema suyo, y me explico. En el caso que el DAO esté atacando a una base de datos internamente implementará una arquitectura trabajando con SqlCommand y SqlTransaction… pero… ¿y si está llamando a Servicios Web? ¿Y si está atacando a ficheros de texto? ¡¡Pues ya lo definirá internamente!! Eso es totalmente transparente a la capa que llama a los DAO.
En el caso del ejemplo hemos implementado llamadas a métodos BeginTransaction, Rollback y Commit en nuestro BaseDAO, que es el que internamente gestiona las transacciones. ¿Cómo puede funcionar esto internamente? Pues por ejemplo manteniendo BaseDAO una lista genérica de de TransaccionDAO, la cual irá aumentando y disminuyendo a medida que los clientes pidan y confirmen transacciones.
Tal vez quede perfilar un poco más cómo maneja BaseDAO ese pool de transacciones… eso lo haremos en otro artí­culo. Dejo alguna pregunta abierta ¿qué ocurre si creamos transacciones y luego no hacemos rollback o commit de ellas? Deberiamos currarnos la caducidad de dichas transacciones…
Saludos.
Miguel.

3 thoughts on “Haciendo nuestros Data Access Object (DAO) transparentemente transaccionales”

  1. Excelente artí­culo!!! respecto a la pregunta final pienso que tanto el commit y rollback son obligatorios en toda transacción desde mi punto de vista, el problema que planteas es si brindarle al programador la libertad de elegir hacer o no uso de ellas en una transacción, lo cual implica que la clase encargada de manejar las transacciones (TransactionDAO) mantenga por un parte una lista de transacciones y un mecanismo automático para ejecutar dichas sentencias en un plazo determinado de tiempo; esto nos obliga a replantear la pregunta inicial y centrarnos en las opciones disponibles para implementar dicho mecanismo.
    Salu2

  2. Hola Miguel:
    Lo primeor darte las gracias por el Blog, me está resolviendo muchas dudas. He estado intentando implementar una aplicación basándome en los consejos que das en la serie de post sobre DAO. En mi caso quiero realizar de forma lo mas transparente posible la eliminación, modificación,etc de datos contra la base de datos. Quiero usar transacciones pues tengo bastantes tablas relacionadas entre sí­. La cuestión es que no consigo implementar esas transacciones de forma separada a cada CocheDTO (por poner un ejemplo), pues a lo sumo puedo pasar la transaccion, pero esto me impide usar using a la hora de abrir la conexion. Me gustarí­a saber si me puedes dar una idea un poco más detallada (si es algíºn ejemplo mejor) de como implementar BaseDAO y opcionalmente TransactionDAO.
    Gracias.

  3. Hola ílvaro,
    Por darte alguna pista de cómo se puede implementar el TransactionDAO, a partir de los métodos que expone la clase BaseDAO puedes manejar internamente una lista donde vayas cargando los diferentes SQLTransaction en su interior. BaseDAO te va a devolver íºnicamente ids de las transacciones, que finalmente no son más que el í­ndice que ocupa el SQLTransaction en la lista.
    Es una idea muy sencilla, pero que te ayuda a abstraer a los DAO de la transacción que estás utilizando, ayudándote incluso a manejar transacciones distribuidas, ya que el referirte a una transacción desde tu capa de presentación se va a basar íºnicamente en manejar un id que le puedes ir pasando a la capa de servicios distribuida. Ten en cuenta que un SQLTransaction no lo puedes enviar mediante un parámetro en un servicio web al tratarse de un objeto no serializable.
    Un saludo.
    Miguel.

Leave a Reply to Miguel Cancel reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.