Зачем помещать слой DAO поверх слоя персистентности (например, JDO или Hibernate)

объекты доступа к данным (DAOs) являются общим шаблоном проектирования и рекомендованы Sun. Но самые ранние примеры Java DAOs напрямую взаимодействовали с реляционными базами данных - они, по сути, занимались объектно-реляционным отображением (ORM). В настоящее время я вижу DAOs поверх зрелых фреймворков ORM, таких как JDO и Hibernate, и мне интересно, действительно ли это хорошая идея.

Я разрабатываю веб-сервис, используя JDO в качестве слоя персистентности, и рассматриваю, следует ли вводить DAOs. Я предвижу проблему при работе с определенным классом, который содержит карту других объектов:

public class Book {
    // Book description in various languages, indexed by ISO language codes
    private Map<String,BookDescription> descriptions;
}

JDO достаточно умен, чтобы сопоставить это с ограничением внешнего ключа между таблицами "книги" и "книжные описания". Он прозрачно загружает объекты BookDescription (используя ленивую загрузку, я считаю) и сохраняет их, когда объект Book сохраняется.

Если бы я должен был ввести "уровень доступа к данным" и написать класс, такой как BookDao, и инкапсулировать весь код JDO в этом случае не будет ли прозрачная загрузка дочерних объектов JDO обходить уровень доступа к данным? Для согласованности не следует загружать и сохранять все объекты BookDescription через какой-либо объект BookDescriptionDao (или BookDao.метод loadDescription)? Тем не менее, рефакторинг таким образом сделал бы манипулирование моделью излишне сложным.

Итак, мой вопрос: что не так с вызовом JDO (или Hibernate, или любого ORM, который вам нравится) непосредственно в бизнесе слой? Его синтаксис уже довольно лаконичен, и это хранилище данных-агностик. В чем преимущество, если таковое имеется, инкапсуляции его в объекты доступа к данным?

10 ответов


Это зависит от целей вашего слоя. Вы помещаете абстракцию, чтобы предоставить другой набор семантики над другим набором. Как правило, дальнейшие слои существуют для упрощения чего-то, такого как развитие будущего обслуживания. Но у них могут быть и другие применения.

например, слой DAO (или обработка сохраняемости) над ORM-кодом предоставляет специализированные функции восстановления и обработки ошибок, которые вы не хотели загрязнять бизнес-логику.


Вы делаете какие-то очки. Но я все же использую слой Dao, вот почему:

  1. доступ к базе данных вызовы к удаленной системе. Во всех таких случаях (также web-сервис, ajax и др...), гранулярность взаимодействия должна быть достаточно большой. Многие мелкие звонки убивали производительность. Эта необходимость производительности часто требует другого представления системы или слоя (здесь, слой Dao).

  2. Иногда ваша операция настойчивости только для загрузки/сохранения/удаления объекта. Один уникальный Dao (или суперкласс ; рассмотрим дженерики) может нести ответственность за это, поэтому вам не нужно кодировать эти методы снова и снова.
    Но часто, у вас также есть конкретные потребности, такие как выполнение определенного запроса, который не создается автоматически ORM. Там вы кодируете свою конкретную потребность с помощью определенного метода Dao (повторное использование часто возможно).
    Наличие регулярных и конкретных потребностей в одном слое позволяет повторно использовать (для например, перехват может гарантировать, что соединение с базой данных открыто / запущено, когда это необходимо).


DAO потерял свое значение с течением времени.

в дни J2EE, когда он стал популярным шаблоном, DAO был классом, в котором вы могли одновременно обслуживать несколько источников данных - базу данных одним поставщиком, базу данных другим, файл - и предоставлять одно место для обертывания запросов для передачи данных.

было много возможностей для повторного использования, поэтому объект DAO для конкретной сущности может также расширить абстрактный DAO, в котором размещался повторно используемый материал, который сам по себе реализован интерфейс DAO.

Post-J2EE/EJB, шаблоны DataMapper и DataSource (или для простых систем, ActiveRecord) стали популярными для выполнения той же роли. Однако DAO стал модным словом для любого объекта, связанного с настойчивостью.

в наши дни термин "DAO", к сожалению, стал синонимом"класса, который позволяет мне общаться с моей базой данных".

с ORM / JPA большая часть обоснования истинного, J2EE era DAO предоставляется из коробка.

в случае последнего шаблона источника данных EntityManager JPA сродни источнику данных, но обычно предоставляется через определение XML PersistenceUnit и создается через IoC.

методы CRUD, которые когда-то жили в DAO или Mapper, теперь могут быть предоставлены точно один раз с использованием шаблона репозитория. Нет необходимости в AbstractDAO - продукты ORM достаточно умны, чтобы принять объект() и знать, где он сохраняется.


при использовании инструмента ORM, такого как JDO или JPA, DAOs являются анти-шаблоном. В этом случае создание "слоя доступа к данным" совершенно не нужно и только добавит дополнительный код и сложность в кодовую базу, что затруднит ее разработку и обслуживание.

основываясь на моем предыдущем опыте, я бы рекомендовал использовать простой статический фасад, скажем Persistence, чтобы обеспечить простой в использовании, API высокого уровня для операций, связанных с сохраняемостью.

затем, вы можете использовать статический импорт чтобы получить легкий доступ к этим методам в любом месте, где они полезны. Например, у вас может быть следующий код:


    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);
    ...

код выше как легко и просто как возможный, и может быть легко блоком испытанным.


одно слово: сделки

возьмите ситуацию, когда я должен выполнить две операции обновления данных в одной транзакции. Эти операции вместе образуют логическую единицу работы. Моя бизнес-логика хочет выразить себя в терминах этой единицы работы, и она не хочет беспокоиться о границах транзакций.

поэтому я пишу DAO. Возьмите этот псевдо-код, используя Spring transactions и hibernate:

отредактировано для удаления HQL, который был оскорбляя @Roger так много, но это не имело отношения к делу

@Transactional
public void doUnitOfWork() {
  // some persistence operation here
  // some other persistence operation here
}

моя бизнес-логика вызывает doUnitOfWork (), который начинает транзакцию, выполняет обе операции сохранения, а затем фиксирует. Он не знает и не заботится о транзакции или о том, какие операции выполняются.

кроме того, если DAO реализует интерфейс с методом doUnitOfWork (), то бизнес-логика может кодировать интерфейс, что облегчает его единицу тест.

В Общем, Я всегда оберните мои операции доступа к данным в DAO и ударить интерфейс вокруг него.


Я считаю, что большинство DAOs добавляются людьми по histerical (историческим ;] ) причинам. Вы правы в том, что они изначально подразумевались как удобная инкапсуляция клея SQL, необходимого для выполнения операций CRUD в дни до ORM. В настоящее время, с очевидной настойчивостью, их роль в значительной степени избыточна.

что сейчас уместно, так это концепции репозиториев и сервисов:

хранилище: Класс, хранящий коллекцию реализованных методов запроса в конкретном коде ORM (например, Hibernate или JDO)

обычно вы можете создать абстрактный репозиторий базового класса, а затем предоставить конкретную реализацию ORM, в которую вы реализуете все методы запроса в коде, специфичном для вашего ORM. Самое замечательное в этом подходе заключается в том, что вы можете создать mockrepository implemenation, чтобы помочь протестировать приложение без использования БД.

сервис: Класс, хранящий коллекцию методов, которые могут управлять нетривиальными изменения / дополнения к объектной модели (обычно независимый код ORM).

Это помогает сохранить ваше приложение в значительной степени независимым от ORM - для переноса приложения на другой ORM действительно включает только реализацию нового класса репозитория ORM.


Я полагаю, что шаблон "DAO class per entity" абсолютно избыточен для уровня данных, управляемого ORM. Вместо этого слой DAO должен состоять из набора универсальных методов CRUD, работающих с произвольными классами сущностей, и большого количества методов, выполняющих более сложные операции с данными. Если функциональность достаточно велика, то слой DAO должен быть разделен на несколько классов на основе критериев домена, что делает подход более похожим на Сервис-Ориентированная Архитектура.


целью всего этого введения в слои было сделать ремонтопригодность легкой и простой.

  1. Уровень Доступа К Данным
  2. Бизнес-Уровня
  3. Внешний Вид

цель 1-го уровня (уровень доступа к данным) состоит в том, чтобы иметь дело с логикой базы данных и предотвратить бизнес-уровень от знания любой из деталей БД.
Уровень доступа к данным использует POJO или EJBS (DAO) для реализации IoC, а POJOEJBs использует Hibernate или ORM сопоставление, чтобы фактически иметь дело со слоем базы данных.
Итак, если вы хотите, чтобы ваша бизнес-логика не заботилась о том, что, что и как база данных используется, доступна и обновляется, и вы хотите, чтобы DAO позаботился об этом
DAO может поддерживать логику изменения различных таблиц для поддержки работы, делая несколько вызовов hibernate.
По сути, вы реализуете многоуровневый подход на уровне доступа к данным, снова нарушая его функциональность в двух слоях aka DAO и Зимовать.


Если вы используете ORM:наслаждайтесь их прозрачной поддержкой настойчивости! Не используйте DAOs для упаковки API ORM. Как здесь хорошо сказано, даос-до Ормов. ORMs ввела концепции из OODBMS, такие как прозрачная настойчивость и настойчивость по достижимости. Вы должны воспользоваться этим, потому что это сделает вашу жизнь проще и код красивый. Предположим, вы моделируете отделы и сотрудников... Одним из вариантов использования может быть создание нового отдела, создание нового сотрудник и добавление сотрудника в отдел... что бы ты сделал?

//start persistence context
...
Department dept1 = new Department("department1");
dept1.addEmployee(new Employee("José", 10503f));

em.persist(dept1);
...
//close persistence context

отдел, сотрудник и их отношение являются постоянными в настоящее время.

предположим, теперь вам нужно добавить существующего сотрудника и существующий отдел... что бы ты сделал? довольно просто:

//start persistence context
...
Department aDepart = hibernateSession.load(Department.class, dId);
Employee anEmployee = hibernateSession.load(Employee.class, eId);

aDepart.addEmployee(anEmployee);     
...
//close persistence context

довольно просто благодаря прозрачной настойчивости и настойчивости по достижимости, которые Hibernate (как и другие ORMs) реализует. Никаких даосов.

просто код модель домена и думать, как вы упорствуете в памяти. С хорошей стратегией отображения ORM будет прозрачно сохраняться, что вы делаете в памяти.

дополнительные примеры здесь: http://www.copypasteisforword.com/notes/hibernate-transparent-persistence http://www.copypasteisforword.com/notes/hibernate-transparent-persistence-ii


на самом деле это должно быть проще, чем все эти ответы. Эти узоры все о слоях. Вы не хотите, чтобы круговые ссылки на вас создавали слои, которые могут знать только о вещах над ними. Вы хотите, чтобы ваш UICode мог ссылаться на все службы, ваш сервисный код мог ссылаться на все DAOs.

  1. Дао
  2. сервис
  3. UICode

с POJOs передается сверху на дно.