2009-09-04 5 views
32

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

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

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

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

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

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

+2

Спасибо за ответы до сих пор. Я вижу, что в некоторых случаях шаблон DAO может решить * немедленную * потребность, например, когда вам нужен специальный код для извлечения объекта, обработка ошибок и т. Д. Но в других случаях это больше теоретические дебаты («поддерживаемость» одного человека «это преждевременная абстракция другого человека» без окончательного ответа. –

+0

Чтобы дать некоторый задний вопрос, мой интерес к DAO был изначально как средство для решения неотложной задачи, а именно инъекции зависимостей в объекты, загруженные JDO. Но с тех пор я нашел то, что я считаю лучшим решением: метод AddInstanceLifecycleListener() JDO. –

+3

Прошло несколько месяцев ... в конце концов я * сделал * введя уровень доступа к данным поверх JDO, чтобы инкапсулировать аспекты безопасности (ограничение того, какие объекты видимы или редактируются текущим пользователем). –

ответ

9

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

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

12

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

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

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

+0

О пункте 1: DAO не нужны для этого, совсем нет. Пример 2: DAO не нужны для этого; Я закодировал сотни конкретных запросов, не используя классы DAO, и методы запросов были повторно использованы. –

6

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

Основываясь на моем предыдущем опыте, я бы рекомендовал использовать простой статический фасад, скажем 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); 
    ... 

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

+4

Я рад видеть, что вы называете DAO анти-шаблоном! Но ... не является ли ваш статический фасад стойкости концептуально почти тем же, что и DAO? Я не вижу преимущества абстрагирования однострочных методов JDO для однострочных статических методов, а абстракция «протекает», потому что для этого требуется, чтобы язык запросов лежал в основе ORM. –

+1

Все по-другому, потому что концептуально Facade является упрощенным фронтом для более крупного и сложного API; что в точности соответствует API-интерфейсам Hibernate, JPA и JDO. Эти методы на самом деле не однострочные. Они также должны открыть/получить соответствующий объект рабочей единицы (сеанс Hibernate, JPA EntityManager) из ThreadLocal (в веб-приложении); может быть некоторый код обработки исключений; и так далее. Я не обращаю внимания на язык запросов, потому что реальная цель - упростить клиентский код, а не допускать переносимость. Но я бы рекомендовал избегать HQL, теперь со стандартным JPA QL (или JDO QL). –

+0

Обратите также внимание на то, что целью DAO является инкапсуляция кода доступа к данным. Это имеет смысл с JDBC, но не с API ORM высокого уровня, которые сами инкапсулируют реальный код доступа к данным. Вот почему я говорю, что DAO является анти-шаблоном при использовании JDO или JPA. –

0

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

  1. DAO
  2. служба
  3. Uicode

причем POJOs передается сверху вниз.

+0

Но что это связано с использованием DAO или нет? Слои и DAO являются независимыми концепциями, хотя DAO обычно помещаются в выделенный слой. –

5

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

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

Поэтому я пишу DAO. Возьмите этот псевдо-код с помощью Spring транзакций и зимуют:

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

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

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

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

Как правило, I всегда переносит мои операции доступа к данным в DAO и ударяет по нему интерфейс.

+1

DAO не должны иметь код транзакции, за исключением особых ситуаций, когда правило общей демаркации транзакций не применяется. (Разумеется, бизнес-логика не должна содержать такой код.) –

+0

Я не согласен глубоко. – skaffman

+0

Код типа "getHibernateTemplate(). Execute (" некоторый HQL здесь "); ужасно. Он многословен и раскрывает тот факт, что Hibernate используется (что должно быть просто просто детальностью реализации, теперь, когда у нас есть JPA). Создание дополнительных интерфейсов только для тестирования - это устаревшая практика. Я могу полностью тестировать любой вид Java-кода без них, с короткими и элегантными тестами JUnit/TestNG. –

3

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

Что сейчас уместна концепции Хранилища и услуги:

Repository: Класс, который хранит коллекцию методов запросов, реализованных в ОРМ конкретного кода (например, Hibernate или СДО)

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

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

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

+1

Спасибо за это. На первый взгляд трудно понять, как шаблон хранилища на самом деле отличается от DAO, но, очевидно, существует определенная разница в назначении (см., Например, http://warren.mayocchi.com/2006/07/27/repository-or-dao/) –

3

Я полагаю, что шаблон «Класс DAO для объекта» абсолютно избыточен для слоя данных, управляемого ORM. Вместо этого, слой DAO должен состоять из набора наборов методов CRUD с одним набором возможностей, которые работают с произвольными классами сущностей и большим количеством методов, которые выполняют более сложные операции с данными. Если функциональность достаточно большая, то уровень DAO должен быть разделен на несколько классов на основе критериев домена, что делает подход более похожим на сервис-ориентированную архитектуру.

+1

Я согласен - «класс DAO на сущность» никогда не должен быть замечен снова, кроме как палеонтологи, которые выкапывают остатки нашей цивилизации в 305 году. Другая аналогичная концепция - «DTO» (объект передачи данных). В большинстве ORM, обеспечивающих подключение/отсоединение, нет причин, по которым вы не можете использовать отдельные объекты объекта для передачи данных. Нет необходимости писать дополнительный класс для каждого класса сущности просто для его передачи (например, сортировка объектов, сериализация в/из JSON и т. Д.). Некоторые утверждают, что DTO защищают вас от изменений модели, но большинство изменений модели требуют, чтобы DTO был обновлен в любом случае !! – Volksman

10

DAO со временем потерял смысл.

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

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

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

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

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

В случае последнего шаблона DataSource, EntityManager JPA сродни DataSource, но обычно предоставляется с помощью определения PersistenceUnit XML и создается через IoC.

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

+2

+1 Спасибо за каталогизацию этого семейства альтернативных шаблонов (DAO, DataSource, ActiveRecord, Repository). Заставляет меня задаться вопросом, какая следующая большая вещь будет ... :) –

+0

Если бы я мог правильно поместиться, ваша идея DAO с современными продуктами ORM является неправильным способом определения DAO-шаблона. Кроме того, вы предлагаете комбинировать шаблон репозитория с инструментами ORM, которые выглядят достаточно справедливо для поиска операций. Но если есть необходимость обновить операцию, которая не связана с шаблоном репозитория, должен быть добавлен дополнительный объект, например Abstract DAO (или что-то другое, что вы называете сложным фасадом и т. Д.), –

2

Целью всего этого введения в слои было упрощение и упрощение обслуживания.

  1. Data Access Layer
  2. Business Layer
  3. Presentation Layer

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

1

Если вы используете ORM: Наслаждайтесь их Прозрачной поддержкой сдерживания! Не используйте DAO для переноса API ORM. Как хорошо сказано здесь, DAO находятся перед 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) реализует. Нет DAO вообще.

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

Другие примеры здесь: http://www.copypasteisforword.com/notes/hibernate-transparent-persistence http://www.copypasteisforword.com/notes/hibernate-transparent-persistence-ii