architecture - ¿Por qué poner una capa DAO sobre una capa de persistencia(como JDO o Hibernar)
persistence data-access-layer (10)
Los objetos de acceso a datos (DAO) son un patrón de diseño común y recomendado por Sun. Pero los ejemplos más antiguos de DAO de Java interactuaron directamente con las bases de datos relacionales: en esencia, realizaban el mapeo relacional de objetos (ORM). Hoy en día, veo los DAO sobre frameworks ORM maduros como JDO e Hibernate, y me pregunto si realmente es una buena idea.
Estoy desarrollando un servicio web usando JDO como capa de persistencia, y estoy considerando si introducir o no DAO. Preveo un problema cuando se trata de una clase particular que contiene un mapa de otros objetos:
public class Book {
// Book description in various languages, indexed by ISO language codes
private Map<String,BookDescription> descriptions;
}
JDO es lo suficientemente inteligente como para asignar esto a una restricción de clave externa entre las tablas "LIBROS" y "LIBRO DE LIBRES". Carga de forma transparente los objetos BookDescription (utilizando la carga diferida, creo) y los persiste cuando persiste el objeto Book.
Si tuviera que introducir una "capa de acceso a datos" y escribir una clase como BookDao, y encapsular todo el código JDO dentro de esto, entonces ¿la carga transparente de JDO de los objetos secundarios no estaría eludiendo la capa de acceso a datos? Por coherencia, ¿no deberían cargarse y conservarse todos los objetos BookDescription mediante algún objeto BookDescriptionDao (o el método BookDao.loadDescription)? Sin embargo, la refacturación de esa manera haría innecesariamente complicada la manipulación del modelo.
Entonces mi pregunta es, ¿qué hay de malo en llamar JDO (o Hibernate, o cualquier ORM que te apetezca) directamente en la capa de negocios? Su sintaxis ya es bastante concisa, y es independiente del almacén de datos. ¿Cuál es la ventaja, si alguna, de encapsularlo en Objetos de acceso a datos?
Creo que la mayoría de los DAO son agregados por personas por razones históricas (históricas;). Tiene razón en que originalmente estaban destinados a una conveniente encapsulación del pegamento SQL requerido para realizar las operaciones CRUD en días previos al ORM. Hoy en día, con persistencia transparente, su papel ahora es en gran parte redundante.
Lo que ahora es apropiado son los conceptos de repositorios y servicios:
Repositorio: una clase que almacena una colección de métodos de consulta implementados en código específico de ORM (p. Ej., Hibernate o JDO)
Normalmente puede crear un repositorio de clase base abstracto y luego proporcionar una implementación específica de ORM en la que implemente todos los métodos de consulta en un código específico de su ORM. Lo bueno de este enfoque es que puedes crear una implementación de MockRepository para ayudar a probar tu aplicación sin usar la base de datos.
Servicio: una clase que almacena una colección de métodos que pueden orquestar cambios / adiciones no triviales al modelo de objetos (por lo general, código independiente de ORM).
Esto ayuda a mantener su aplicación en gran medida independiente de ORM: para portar la aplicación a otro ORM realmente solo involucra la implementación de una clase (s) de repositorio específica de ORM.
Cuando se utiliza una herramienta ORM como JDO o JPA, los DAO son un antipatrón. En este caso, crear una "capa de acceso a datos" es completamente innecesario y solo agregará código adicional y complejidad a la base de código, lo que hará que sea más difícil de desarrollar y mantener.
En función de mi experiencia previa, recomendaría el uso de una fachada estática simple, por ejemplo Persistence
, para proporcionar una API fácil de usar y de alto nivel para las operaciones relacionadas con la persistencia.
Luego, puede usar una importación estática para obtener acceso fácil a esos métodos en cualquier lugar que sean útiles. Por ejemplo, podría tener un código como el siguiente:
List<Book> cheapBooks =
find("select b from Book where b.price < ?", lowPriceForBooks);
...
Book b = new Book(...);
persist(b);
...
Book existingBook = load(Book.class, bookId);
remove(existingBook);
...
El código anterior es lo más fácil y simple posible, y se puede probar fácilmente en una unidad.
DAO ha perdido su significado con el tiempo.
Durante los días J2EE cuando se convirtió en un patrón popular, un DAO era una clase donde podía atender simultáneamente múltiples fuentes de datos (una base de datos de un proveedor, una base de datos por otro, un archivo) y proporcionar un lugar único para envolver consultas a comunicarse por datos.
Había muchas posibilidades de reutilización, por lo que un objeto DAO para una entidad en particular podría extender un DAO abstracto que albergaba las cosas reutilizables, que en sí mismo implementaba una interfaz DAO.
Post-J2EE / EJB, los patrones DataMapper y DataSource (o para sistemas simples, ActiveRecord) se hicieron populares para realizar el mismo rol. Sin embargo, DAO se convirtió en una palabra de moda para cualquier objeto relacionado con la persistencia.
Hoy en día, el término ''DAO'' se ha convertido lamentablemente en sinónimo de "una clase que me permite comunicarme con mi base de datos".
Con ORM / JPA, gran parte de la justificación para una verdadera DAO de la era J2EE se proporciona de inmediato.
En el caso de un último patrón de DataSource, el EntityManager de JPA es similar al DataSource, pero generalmente se proporciona a través de una definición XML de PersistenceUnit y se crea una instancia a través de IoC.
Los métodos CRUD que una vez vivieron en un DAO o en un Mapper ahora se pueden proporcionar exactamente una vez con el patrón Repository. No hay necesidad de AbstractDAO: los productos ORM son lo suficientemente inteligentes como para aceptar un objeto () y saber dónde lo está persistiendo.
De hecho, es más simple de lo que todas estas respuestas lo hacen. Estos patrones son todos acerca de las capas. No desea referencias circulares para crear capas que solo puedan conocer las cosas que están por encima de ellas. Desea que su UICode pueda hacer referencia a todos y cada uno de los Servicios, su código de Servicio para poder hacer referencia a todos y cada uno de los DAO.
- DAO
- Servicio
- UICode
con los POJOs pasando de arriba a abajo.
Depende de cuáles sean los objetivos de tu capa. Pone una abstracción para proporcionar un conjunto diferente de semántica sobre otro conjunto. En general, hay capas adicionales para simplificar algunas cosas, como el desarrollo de mantenimiento futuro. Pero podrían tener otros usos.
Por ejemplo, una capa DAO (o manejo de persistencia) sobre un código ORM proporciona funcionalidad especializada de recuperación y manejo de errores que no quería contaminar la lógica comercial.
El propósito de toda esta introducción a las capas fue hacer que el mantenimiento sea fácil y simple.
- Capa de acceso a datos
- Capa empresarial
- Capa de presentación
El objetivo de la 1ª capa (capa de acceso a datos) es tratar con la lógica de la base de datos y evitar que Business Layer conozca alguno de los detalles de la base de datos.
La capa de acceso a datos utiliza POJO o EJB (DAO) para implementar IoC y POJOEJB utiliza Hibernate o mapeo ORM para tratar realmente con la capa de la base de datos.
Por lo tanto, si desea que su lógica empresarial no se preocupe por qué, qué y cómo se usa, acceda y actualice una base de datos, y desea que DAO se encargue de esto.
DAO puede admitir la lógica de cambiar diferentes tablas para admitir la operación haciendo una serie de llamadas de hibernación.
Básicamente, está implementando un enfoque estratificado en Data Access Layer al romper su funcionalidad nuevamente en dos capas, también conocidas como DAO e Hibernate.
Si usa un ORM: ¡ disfrute de su soporte de persistencia transparente ! No use DAO para envolver las API de ORM. Como bien se dijo aquí, los DAO están antes de los ORM. ORMs ha introducido conceptos de OODBMS, como Transparent Persistence y Persistence by Reachability. Tienes que aprovechar eso, porque te hará la vida más fácil y tu código hermoso. Supongamos que está modelando departamentos y empleados ... Un caso de uso podría ser crear un nuevo departamento, crear un nuevo empleado y agregar al empleado al departamento ... ¿qué haría?
//start persistence context
...
Department dept1 = new Department("department1");
dept1.addEmployee(new Employee("José", 10503f));
em.persist(dept1);
...
//close persistence context
El departamento, el empleado y su relación son persistentes ahora.
Supongamos que ahora tiene que agregar un empleado existente al departamento existente ... ¿qué haría? bastante simple:
//start persistence context
...
Department aDepart = hibernateSession.load(Department.class, dId);
Employee anEmployee = hibernateSession.load(Employee.class, eId);
aDepart.addEmployee(anEmployee);
...
//close persistence context
Bastante simple gracias a Transparent Persistence y Persistence by Reachability que Hibernate (como otros ORM) implementa. No DAO en absoluto.
Simplemente codifique su modelo de dominio y piense que persiste en la memoria. Con una buena estrategia de mapeo, el ORM persistirá de forma transparente en la memoria.
Más ejemplos aquí: http://www.copypasteisforword.com/notes/hibernate-transparent-persistence http://www.copypasteisforword.com/notes/hibernate-transparent-persistence-ii
Supongo que el patrón "Clase DAO por entidad" es absolutamente redundante para una capa de datos administrada por ORM. En cambio, la capa DAO debe estar compuesta por un conjunto de métodos CRUD únicos que operan en clases de entidades arbitrarias y una gran cantidad de métodos que realizan operaciones más sofisticadas en los datos. Si la funcionalidad es lo suficientemente grande, entonces la capa DAO se debe dividir en varias clases según los criterios del dominio, lo que hace que el enfoque sea más similar a la Arquitectura orientada a servicios.
Una palabra: transacciones
Tome la situación en la que tengo que realizar dos operaciones de actualización de datos en una sola transacción. Estas operaciones juntas forman una unidad lógica de trabajo. Mi lógica comercial quiere expresarse en términos de esa unidad de trabajo, y no quiere preocuparse por los límites de las transacciones.
Entonces escribo un DAO. Toma este pseudo código usando Spring transactions e hibernate:
editado para eliminar HQL que tanto ofende a @Roger pero que no era relevante para el punto
@Transactional
public void doUnitOfWork() {
// some persistence operation here
// some other persistence operation here
}
Mi lógica de negocios llama a doUnitOfWork (), que comienza una transacción, realiza ambas operaciones de persistencia y luego confirma. No conoce ni se preocupa por la transacción ni por las operaciones que se realizan.
Además, si el DAO implementa una interfaz con el método doUnitOfWork (), la lógica de negocios puede codificar a la interfaz, lo que facilita la prueba unitaria.
En general, siempre cierro mis operaciones de acceso a datos en un DAO, y golpeo una interfaz a su alrededor.
Usted hace algunos puntos. Pero, sin embargo, uso una capa Dao, he aquí por qué:
Los accesos a la base de datos son llamadas a un sistema remoto . En todos estos casos (también web-service, ajax, etc.), la granularidad de la interacción debe ser lo suficientemente grande. Muchas llamadas pequeñas matarían el rendimiento. Esta necesidad de rendimiento requiere a menudo una vista diferente del sistema, o capa (aquí, la capa Dao).
A veces, su operación de persistencia es solo para cargar / guardar / eliminar un objeto. Un único Dao (o una superclase, considere Generics) puede ser responsable de esto, por lo que no tiene que codificar estos métodos una y otra vez.
Pero a menudo, también tiene necesidades específicas, como ejecutar una solicitud específica que no se crea automáticamente por el ORM . Allí, codifica su necesidad específica con un método Dao específico (a menudo es posible reutilizar).
Tener necesidades regulares y específicas en la misma capa permite la reutilización (por ejemplo, la interceptación puede asegurar que la conexión de una base de datos esté abierta / comprometida cuando sea necesario).