2009-04-21 30 views
22

Así que tengo esta cosa genérica de DAO pasando y al valor nominal parece estar bien. Básicamente se basa en la aplicación de muestra CaveatEmptor de los chicos de Hibernate.Patrón DAO: ¿dónde caben las transacciones?

Además de eso, tengo una capa empresarial ... las entrañas de la aplicación. No tiene conocimiento de ninguna implementación específica de DAO.

Todo hasta este punto parece estar bien, hasta que empiezo a pensar en las transacciones. Si las transacciones se dejan al cliente para implementarlas, ¿cómo puedo mantener la agradable separación que tengo entre mis capas? Es decir, estoy usando Hibernate en este momento, y realmente no tengo ganas de agregar transacciones específicas de hibernación a mi código de capa empresarial.

que podría crear una interfaz simple transacción con begin, commit y rollback métodos y pasar una implementación a mi capa de negocio ... pero ... no estoy seguro ...

Así que aquí está el desafío: ¿me pueden recomendar una forma de hacerlo sin usar la palabra Spring (o EJB, o cualquier otro marco adicional)?

Respuesta

12

Recuerdo que Martin Fowler aconseja mantener el control de la transacción en la capa empresarial porque la transacción es un problema comercial. (Si diseña una clase BankAccount, una transacción es parte del idioma del dominio).

Usted puede tratar de implementar un TransactionScope como en .NET funciona algo así

using (TransactionScope ts = new TransactionScope()) 
{ 
    ... 
} 

Es lo mismo que (no exactamente, pero si usted es un hombre de Java, es más explícita a usted)

TransactionScope scope = new TransactionScope(); 
try 
{ 
... 
scope.Commit(); 
} 
catch(Exception ex) 
{ 
    scope.Rollback(); 
    throw; 
} 

para desacoplar la capa de negocio de cualquier tecnología de DAO puede agregar un TransactionFactory en su idioma de dominio, que devuelve un ITransactionScope (una interfaz) que ha definido con un commit y rollback métodos. De esta forma, su capa de dominio no está vinculada a su capa DAO, solo una implementación concreta de TransactionFactory.

ITransactionScope scope = transactionFactory.CreateTransaction(); 
try 
{ 
... 
scope.Commit(); 
} 
catch(Exception ex) 
{ 
    scope.Rollback(); 
    throw; 
} 
+0

Bueno, esto funciona, pero rápidamente terminas con miles de líneas de código duplicadas, para cualquier aplicación comercial realista. –

+0

@Rogerio, no todos los métodos en un objeto comercial son necesariamente una transacción. Si termina con la duplicación, entonces tal vez su dominio no está bien expresado, o puede encontrar una mejor manera de expresarlo. –

+3

Acabo de volver a leer las páginas relevantes sobre las transacciones en el libro PoEAA (71-77), y el autor no recomienda que las transacciones sean un problema comercial (no son * parte del lenguaje comercial, sino una herramienta para *control de concurrencia*). Además, en la práctica, casi todos los métodos involucrados en una operación comercial * deben * ejecutarse en el contexto de una transacción del sistema, incluso si normalmente no tiene una transacción separada para cada método. El punto sigue siendo que una aplicación bien diseñada no debe * tener un código de demarcación de transacción explícito en todas partes, sino solo en uno o dos lugares centrales. –

1

Tiene derecho a que la aplicación sea un buen lugar para coordinar las transacciones, ya que permite la composición de acciones más complejas implementadas por varios servicios/gerentes/o como quiera llamarlas.

Una solución fácil es definir una interfaz ITransaction y utilizar algún tipo de fábrica o DI para ocultar el implementador ITransaction real de su aplicación. Hice mi propio estilo en .net usando nHibernate y esencialmente tengo una clase base que todos mis gerentes (un administrador en este caso contiene lógica comercial para un conjunto lógico de entidades como membresía, orden que podría usar uno o más repositorios). Mi clase base tiene ITransaction BeginTransaction(), que crea dinámicamente un tipo basado en un archivo de configuración.

Esta clase luego trabaja con nHibernate's Session para comenzar y confirmar las transacciones.

1

En el pasado he puesto la lógica de transacción en el DAO raíz para una jerarquía de DAO que coinciden con una jerarquía de Objetos en su modelo que representa una sola entidad sólida en el sistema.

Es decir, si tiene una X que tiene muchas Y, y desea almacenar y recuperar X y su Ys al mismo tiempo que un único objeto compuesto, entonces su DAO para X también debe llamar al DAO para Y. Luego puede poner una transacción en todo lo relacionado con los métodos add() y update() en DAO for X, e incluso hacer que el paquete Y DAO sea privado para ocultarlo de la lógica comercial principal. Es decir, en lugar de la lógica de negocio:

XDAO xDAO = new XDAO(conn); 
xDAO.startTransaction(); 
boolean success = xDAO.add(x); 
if (success) 
    for (Y y : x.getYs()) { 
     success = YDAO.add(y); 
     if (!success) break; 
    } 
if (success) 
    xDAO.commit(); 
else 
    xDAO.rollback(); 

Sólo tendría:

XDAO xDAO = new XDAO(conn); 
xDAO.add(x); 

(con el éxito/commit lógica/rollback interno para que DAO)

Sin embargo, esto no lo hace cubra cada situación, y la suya puede ser diferente (por ejemplo, la mina funciona con JDBC, no sé cómo funciona Hibernate o si es posible allí).

6

En una aplicación web, lo que hago para delimitar transacciones es aprovechar el ciclo de petición/respuesta HTTP, donde cada operación de negocio atómica ejecuta en el ámbito de uno de estos ciclos, en un solo hilo dedicado .

Sea cual sea el marco web utilizado (Struts, JSF, GWT, etc.), normalmente existe una "costura" donde se puede realizar la demarcación de la transacción. En Struts, puede ser una clase de acción básica. En GWT, puede ser una clase base RemoteServiceImpl.

Por lo tanto, use ese punto central de acceso para abrir la transacción (antes de permitir que se ejecute el código específico de la aplicación), y terminarlo con una confirmación si no hay excepciones borradas o una reversión (de lo contrario el código fue ejecutado).

Apliqué esta estrategia extensamente en una aplicación web de negocios grande y compleja, y demostró funcionar muy bien.

3

Quizás esto sea demasiado tarde para una respuesta, pero ¿qué tal crear otra clase para transacciones específicas, que se ubica entre la capa de negocios y la capa de dao? P.ej. si los métodos a() y b() de un DAO se van a ejecutar en una transacción para algún método comercial específico de foo(), entonces crea algo como fooInTransaction() que inicia una transacción y llama a() y b() en él . El método de negocio foo() le delega.

Esto mantendrá limpio el código comercial y la duplicación se puede evitar redefiniendo el tamaño.

Cuestiones relacionadas