что такое dao java
Использование паттерна data access object в клиентском приложении
Авторизуйтесь
Использование паттерна data access object в клиентском приложении
Всем привет! В этой статье я поделюсь своим опытом по работе с базой данных в приложении для Android. Это будет один из вариантов реализации паттерна data access object на языке Java.
Постановка задачи
Заказчик, который занимается предстраховыми осмотрами автомобилей, хочет автоматизировать рабочий процесс. Задача приложения — собирать и передавать на обработку данные об автомобиле.
Проектирование показало, что у приложения будет 3 модуля:
Сущности
Доступ к данным
Выделим для этих сущностей 3 вида доступа. В данном случае это будут следующие интерфейсы:
Далее создадим data access object, который предоставляет доступы. Этот интерфейс будет выглядеть так:
Теперь у нас есть все необходимые виды доступа, но пока они не запрашивают и не изменяют данные.
Модели данных
Готовый осмотр будет состоять из элементов, а элементы из данных. Нужно реализовать возможность получения осмотра по ключу, построение списка черновиков осмотров, списка готовых осмотров, а также возможность удалить осмотр. Таким образом, для всех подсущностей осмотра создадим модели:
Теперь передадим доступам их модели:
Теперь наш data access object готов.
Использование data access object
У нас есть три модуля. Каждый из них получит доступ только к той области базы данных, которая необходима для выполнения его задач.
Для сравнения посмотрите на код, который используется для получения данных, если не использовать DAO:
Обратите внимание, что класс неявно получает зависимость от конкретной реализации SQLite. Более того, он получает доступ ко всем частям базы данных, хотя не должен иметь возможности читать, изменять и удалять данные вне его круга задач. А вот как выглядит реализация аналогичной задачи с применением DAO:
Тестирование
Реализация
Заключение
Используя DAO, можно удобно разделять уровни доступа при работе с базой данных, чётко видеть эти уровни доступа и легко оперировать ими, не привязываясь к конкретной реализации хранения данных. Это позволяет применять такой подход вместе с TDD и точнее настраивать реализацию работы с БД (без изменений в других модулях) или полностью заменить одну реализацию на другую.
Забудьте о DAO, используйте Repository
Недавно задумался о том, чем отличаются паттерны, позволяющие абстрагироваться от работы с хранилищем данных. Много раз поверхностно читал описания и различные реализации DAO и Repository, даже применял их в своих проектах, видимо, до конца не понимая концептуальных отличий. Решил разобраться, закопался в Google и нашел статью, которая для меня разъяснила все. Подумал, что неплохо было бы перевести ее на русский. Оригинал для англочитающих здесь. Остальным интересующимся добро пожаловать под кат.
Data Access Object (DAO) — широко распространенный паттерн для сохранения объектов бизнес-области в базе данных. В самом широком смысле, DAO — это класс, содержащий CRUD методы для конкретной сущности.
Предположим, что у нас имеется сущность Account, представленная следующим классом:
Создадим интерфейс DAO для данной сущности:
Паттерн Repository
Лучшим решением будет использование паттерна Repository. Эрик Эванс дал точное описание в своей книге: «Repository представляет собой все объекты определенного типа в виде концептуального множества. Его поведение похоже на поведение коллекции, за исключением более развитых возможностей для построения запросов».
Вернемся назад и спроектируем AccountRepository в соответствии с данным определением:
Методы add и update выглядят идентично методам AccountDAO. Метод remove отличается от метода удаления, определенного в DAO тем, что принимает Account в качестве параметра вместо userName (идентификатора аккаунта). Представление репозитория как коллекции меняет его восприятие. Вы избегаете раскрытия типа идентификатора аккаунта репозиторию. Это сделает вашу жизнь легче в том случае, если вы захотите использовать long для идентрификации аккаунтов.
Если вы задумываетесь о контрактах методов add/remove/update, просто подумайте об абстрации коллекции. Если вы задумаетесь о добавлении еще одного метода update для репозитория, подумайте, имеет ли смысл добавлять еще один метод update для коллекции.
Однако, метод query является особенным. Я бы не ожидал увидеть такой метод в классе коллекции. Что он делает?
Репозиторий отличается от коллекции, если рассматривать возможности для построения запросов. Имея коллекцию объектов в памяти, довольно просто перебрать все ее элементы и найти интересующий нас экземпляр. Репозиторий работает с большим набором объектов, чаще всего, находящихся вне оперативной памяти в момент выполнения запроса. Нецелесообразно загружать все аккаунты в память, если нам необходим один конкретный пользователь. Вместо этого, мы передаем репозиторию критерий, с помощью которого он сможет найти один или несколько объектов. Репозиторий может сгенерировать SQL запрос в том случае, если он использует базу данных в качестве бекэнда, или он может найти необходимый объект перебором, если используется коллекция в памяти.
Одна из часто используемых реализаций критерия — паттерн Specification (далее спецификация). Спецификация — это простой предикат, который принимает объект бизнес-области и возвращает boolean:
Итак, мы можем создавать реализации для каждого способа выполнения запросов к AccountRepository.
Обычная спецификация хорошо работает для репозитория в памяти, но не может быть использована с базой данных из-за неэффективности.
Для AccountRepository, работающего с SQL базой данных, спецификации необходимо реализовать интерфейс SqlSpecification:
Репозиторий, использующий базу данных в качестве бекэнда, может использовать данный интерфейс для получения параметров SQL запроса. Если бы в качестве бекэнда для репозитория использовался Hibernate, мы бы использовали интерфейс HibernateSpecification, который генерирует Criteria.
SQL- и Hibernate-репозитории не используется метод specified. Тем не менее, мы находим наличие реализации данного метода во всех классах преимуществом, т.к. таким образом мы сможем использовать заглушку для AccountRepository в тестовых целях а также в кеширующей реализации репозитория перед тем, как запрос будет направлен непосредственно к бекэнду.
Мы даже можем сделать еще один шаг и использовать композицию Spicification с ConjunctionSpecification и DisjunctionSpecification для выполнения более сложных запросов. Нам кажется, что данный вопрос выходит за рамки статьи. Заинтересованный читатель может найти подробности и примеры в книге Эванса.
Шаблон DAO в Java
Узнайте, как реализовать шаблон объекта доступа к данным (DAO) в Java, чтобы изолировать уровни персистентности и бизнес-уровни вашего приложения.
1. Обзор
Функциональность этого API заключается в том, чтобы скрыть от приложения все сложности, связанные с выполнением операций CRUD в базовом механизме хранения. Это позволяет обоим слоям развиваться отдельно, ничего не зная друг о друге.
Дальнейшее чтение:
Введение в весенние данные JPA
Обзор типов каскадов JPA/Hibernate
2. Простая Реализация
Чтобы понять, как работает шаблон DAO, давайте создадим базовый пример.
2.1. Класс Домена
Поскольку наше приложение будет работать с пользователями, нам нужно определить только один класс для реализации его модели домена:
Класс User является простым контейнером для пользовательских данных, поэтому он не реализует никакого другого поведения, заслуживающего внимания.
Конечно, наиболее релевантный выбор дизайна, который нам нужно сделать здесь, заключается в том, как изолировать приложение, использующее этот класс, от любого механизма сохранения, который может быть реализован в какой-то момент.
Ну, это именно та проблема, которую пытается решить шаблон DAO.
2.2. API DAO
Давайте определим базовый слой DAO, чтобы мы могли увидеть, как он может полностью отделить модель домена от уровня персистентности.
2.3. Класс UserDao
Давайте определим пользовательскую реализацию интерфейса Dao :
Конечно, легко рефакторировать другие методы, чтобы они могли работать, например, с реляционной базой данных.
Хотя оба класса User и UserDao сосуществуют независимо в одном приложении, нам все равно нужно посмотреть, как последний можно использовать для сохранения уровня персистентности, скрытого от логики приложения:
3. Использование шаблона С JPA
Среди разработчиков существует общая тенденция полагать, что выпуск JPA снизил функциональность шаблона DAO до нуля, поскольку шаблон становится просто еще одним уровнем абстракции и сложности, реализованным поверх того, который предоставляется менеджером сущностей JPA.
3.1. Класс JpaUserDao
То JpaUserDao класс способен работать с любой реляционной базой данных, поддерживаемой реализацией JPA.
Кроме того, если мы внимательно посмотрим на класс, мы поймем, как использование Composition и Dependency Injection позволяет нам вызывать только методы entity manager, требуемые нашим приложением.
Проще говоря, у нас есть специализированный API для конкретного домена, а не API всего менеджера сущностей.
3.2. Рефакторинг пользовательского класса
В этом случае мы будем использовать Hibernate в качестве реализации JPA по умолчанию, поэтому мы соответствующим образом рефакторингуем класс User :
3.3. Программная загрузка менеджера сущностей JPA
В большинстве случаев мы достигаем этого с помощью типичного “persistence.xml” файл, что является стандартным подходом.
3.4. Класс UserApplication
Наконец, давайте проведем рефакторинг исходного Пользовательского приложения класса, чтобы он мог работать с экземпляром JpaUserDao и выполнять операции CRUD над сущностями User :
Даже если пример действительно довольно ограничен, он остается полезным для демонстрации того, как интегрировать функциональность шаблона DAO с той, которую предоставляет менеджер сущностей.
Кроме того, мы могли бы поменять MySQL на любую другую СУБД (и даже на плоскую базу данных) в дальнейшем, и все же наше приложение продолжало бы работать, как ожидалось, благодаря уровню абстракции, обеспечиваемому интерфейсом Dao и менеджером сущностей.
4. Заключение
В этой статье мы подробно рассмотрели ключевые концепции шаблона DAO, как реализовать его в Java и как использовать его поверх менеджера сущностей JPA.
Заметки программистера
Страницы
Java. Реализация шаблона DAO
Пример для статьи.
Обе таблицы содержат суррогатные первичные ключи, не смотря на то, что в случае с таблицей Group на роль первичного ключа идеально подходит номер группы. Вообще, особенной проблемы использовать номер группы в качестве первичного ключа нет. Но это не лучшая практика, тк ни что не вечно под луной и то, что нам кажется неизменным на этапе проектирования, может в конце концов измениться и принести нам много головной боли. Впрочем, этот вопрос не имеет никакого отношения к статье.
Вот пара простых sql скриптов для создания и наполнения базы данных:
Полигон для испытаний.
Вернемся к DAO.
Первый способ. Храним значение внешнего ключа.
Такое решение, вообще говоря, не далеко уводит нас от реляционного представления данных, но может оказаться вполне приемлемым в простых проектах, в силу своей ясности и простоты реализации.
Именно с этого решения мы начнем реализацию слоя DAO.
Библиотека DAO. Начало
В качестве базы данных я использую MySQL и предлагаю простейшую реализацию слоя DAO для работы с этой СУБД:
Реализованных на данный момент методов достаточно, чтобы отобразить список всех студенческих групп и даже немножко больше.
Напишем простейший тест, чтобы убедиться в корректности реализации:
Минутка рефакторинга
Как вы думаете, сильно ли будут отличаться одноименные методы в StudentDao? Вероятно нет.
Обратите внимание на то, как составляется запрос на получение записи по первичному ключу. В общем случае такая практика может оказаться не приемлемой, тогда соответствующий запрос нужно будет получать аналогично Select из специального метода.
Создание, удаление, обновление
Обратите внимание на то, что объекты, переданный на вход метода и полученный на его выходе, совершенно разные! При желании этого можно избежать, но пока оставим как есть.
Полный код класса AbstractJDBCDao :
Кто отвечает за идентификатор?
Главным недостатком такого решения является необходимость изменения ссылки на объект после вызова метода persist:
Минутка рефакторинга затягивается
Начали улавливать идею?
Тесты всему голова
После столь большой работы по рефакторингу кода, самое время проконтролировать его корректность. Для получающегося унифицированного решения нужен не менее унифицированный тест. Мы на самом деле можем для каждого дао объекта проконтролировать стандартные CRUD операции, за исключением операции обновления, тк в общем случае мы не знаем какие есть поля у соответствующего объекта доменной сущности.
Пример реализации теста для MqSql фабрики:
Подытожим
61 комментарий :
Офигенная статья, жду следующей части, хотя попробую сам реализовать
Хорошая статья. Но ваш код пестрит raw type. Что не есть хорошо.
К сожалению Вы правы, но я не могу выделить время на исправление этого недостатка в обозримом будущем. Огромное Вам спасибо за то, что обратили внимание на эту проблему. (чтобы понять в чем суть замечания, стоит прочесть статью http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it)
рад, что пригодилась
подскажите, на каком слое реализовывать управление транзакциями: если требуется вызов методов из разных DAO в одной транзакции? Спасибо!
Я бы посоветовал Вам прочесть статью о стратегиях управления транзакциями: http://www.ibm.com/developerworks/ru/library/j-ts2/.
На сколько я понимаю, Ваш случай, это стратегия клиентского дирижирования (client orchestration transaction strategy);
Совет пригодился. Спасибо за отклик!
Спасибо за статью, подскажите, в чем сделаны таблички, которые Вы показываете какие свойства и методы есть у класса?
Средствами Intellij Idea
Тоже в ней работаю, подскажите, пожалуйста, где можно найти такое схематическое представление классов. Спасибо!
Дело в том, что у вас скорее всего Community версия. В версии Ultimate диаграмы строются комбинациями Ctrl+Alt+U или Ctrl+Alt+Shift+U
Спасибо, именно это мне и нужно. Поставил уже ультимейт )
Подскажите, пожалуйста. Я не волшебник, я еще только учусь. Объект, какого из классов и интерфейсов, созданных вами, мне вызывать в методе мэйн, чтобы работать с дао? и какие параметры указывать? Я сделал дао по вашему образцу, в тестах все работает. но я не знаю через какой объект мне его использовать. Вызывать операции CRUD.
Вначале вы должны создать фабрику, а потом через нее создавать Dao объекты для той сущности, над которой планируете выполнять CRUD операции. Примерно так:
DaoFactory factory = new MySqlDaoFactory();
Connection connection = factory.getContext();
GenericDao dao = factory.getDao(connection, Group.class);
Огромное спасибо за ответ! Статья очень помогла в изучении java, и, в частности, в изучении DAO паттерна. Автор молодец! Так держать!
Этот комментарий был удален автором.
Спасибо, все работает. Правда, в файл DaoFactory затесались «очепятки». Вот скачанный код:
********************************
package ru.dokwork.daotalk.dao;
import java.sql.Connection;
import java.sql.SQLException;
/** Фабрика объектов для работы с базой данных */
public interface DaoFactory <
public interface DaoCreator <
public GenericDao create(Context context);
>
/** Возвращает подключение к базе данных */
public Context getContext() throws PersistException;
Прочитайте внимательно статью. Это не опечатка.
А можете подсказать что значит
import ru.dokwork.daotalk.domain.Group;
?
Я работаю в Eclipse, а код похоже был написан в Intellij.
Что такое Group, откуда оно берётся, что там и как его подключить?
Извините, а можно пример описания этого класса?
В конце статьи есть ссылка на исходный код. В частности на интересующий Вас класс: https://bitbucket.org/dok/daotalk/src/bbe5f2efb7863cc292d7d5e7e526f7ae2c15a3f3/src/main/java/ru/dokwork/daotalk/domain/Group.java
Добрый день, подскажите пожалуйста. Я как понимаю в вашем коде запись в базу данных выполняется с помощью dao.create() в котором запускается конструктор по умолчанию для создания объекта(группы). А как же выполнить заполнение полей до записи?
Вы можете самостоятельно создать и инициализировать объект, а затем сохранить его в базу методом dao.persist()
Точно, очевидно ж вроде. Смутил метод create(), не очень понятно для чего он (зачем сохранять объект инициализируемый конструктором по умолчанию)
Здравствуйте.
Интересует вопрос, а что делать если мне нужны какие-то составные данные, к примеру результат селекта десятка таблиц (я не преувеличиваю, у нас сложная логика работы с бд). Ну и понятно что это будет исключительно выборка, т.е. делать Data Object (aka Value Object, aka Entity) под каждую выборку?
Данный подход предполагает что да, под каждую выборку свой объект. Но если речь идет только о выборке, описанное в статье решение может быть оверхедом.
На мой взгляд оба вопроса крайне субъективны, но раз спросили, вот мой взгляд:
1) Я бы не стал зашивать пути к файлам с иконками в таблицу, тк они не имеют отношения к доменной области и могут быть неоднократно изменены и перенесены, а то и вовсе вы решите от картинок отказаться 🙂
2) Да, для несвязных сущностей разные DAO
1) Ну хорошо не в таблицу, а в DAO? В статье оригинала сказано, что хранилищем может выступать что угодно, а не только БД, файловая система например.
2) А близкие по смыслу DAO вы как-то группируете? Может еще какой-то сервис поверх DAO? Чтобы вся работа со студентами и прилегающими объектами координировалась некоторым единым объектом?
2) никак 🙂 на практике чаще используется ORM, и в силу того, что там поддерживаются отношения многие-ко-многим, группировать дао не приходится. Достаточно использовать только те, которые нужны конкретному сценарию бизнеслогики. Но в любом случае городить что-то для объединения дао я бы не стал и на практике присмотрелся к более гибким инструментам вроде ORM или чего-то на подобие Slick.
они закрываются там, где открываются, т.е. за рамками паттерна.
Хорошо: мы убрали зависимость StudentDAO от GroupDAO, и вытягиваем нужный нам Group через getDependence(Group.class, Key).
Но эта зависимость никуда не ушла: она просто перекочевала в наш DaoFatory. Если у нас будет 1000 обьектов, то наша DaoFatory превратиться в божественный обьект, а это антипаттерн, причем более плохой, чем зависимость StudentDAO от GroupDAO.
Огромное спасибо за ответ.
Паттерн Data Access Object
1 Контекст
Способ доступа к данным бывает разным и зависит от источника данных. Способ доступа к персистентному хранилищу, например к базе данных, очень зависит от типа этого хранилища (реляционные базы данных, объектно-ориентированные базы данных, однородные или «плоские» файлы и т.д.) и от конкретной реализации.
2 Проблема
Для доступа к данным, расположенным в системе управления реляционными базами данных (RDBMS), приложения могут использовать JDBC API. JDBC API предоставляет стандартный механизм доступа и управления данными в персистентном хранилище, таком как реляционная база данных. JDBC API позволяет в J2EE-приложениях использовать SQL-команды, являющиеся стандартным средством доступа к RDBMS-таблицам. Однако, даже внутри среды RDBMS фактический синтаксис и формат SQL-команд может сильно зависеть от конкретной базы данных.
Для различных типов персистентных хранилищ существует еще большее число вариантов. Механизмы доступа, поддерживаемые API и функции отличаются для различных типов персистентных хранилищ, таких как RDBMS, объектно-ориентированные базы данных, плоские файлы и т.д. Приложения, которым нужен доступ к данным, расположенным на традиционных или несовместимых системах (например, мэйнфреймы или B2B-службы), часто вынуждены использовать патентованные API. Такие источники данных представляют проблему для приложений и могут потенциально создавать прямую зависимость между кодом приложения и кодом доступа к данным. Когда бизнес-компонентам (компонентам управления данными, сессионным компонентам и даже презентационным компонентам, таким как сервлеты и вспомогательные объекты для JSP-страниц) необходим доступ к источнику данных, они могут использовать соответствующий API для получения соединения и управления этим источником данных. Но включение кода для установления соединения и доступа к данным в код этих компонентов создает тесную связь между компонентами и реализацией источника данных. Такая зависимость кода в компонентах может сделать миграцию приложения от одного типа источника данных к другому трудной и громоздкой. При изменениях источника данных компоненты необходимо изменить.
3 Ограничения
Компоненты управления данными с управляемой компонентом персистенцией, сессионные компоненты, сервлеты и другие объекты, такие как вспомогательные объекты для JSP-страниц, должны получать и сохранять информацию в персистентных хранилищах и других источниках данных, например традиционных системах, B2B, LDAP и т.д.
Для извлечения или сохранения данных во внешних и/или традиционных системах компоненты обычно используют патентованные API.
Включение в компоненты специфических механизмов доступа и API прямо влияет на переносимость компонентов.
Компоненты должны быть прозрачны для реальной реализации персистентного хранилища или источника данных и обеспечивать легкую миграцию на продукт другого поставщика, на другой тип хранилища и на другой тип источника данных.
4 Решение
Используйте Data Access Object (DAO) для абстрагирования и инкапсулирования доступа к источнику данных. DAO управляет соединением с источником данных для получения и записи данных.
DAO реализует необходимый для работы с источником данных механизм доступа. Источником данных может быть персистентное хранилище (например, RDBMS), внешняя служба (например, B2B-биржа), репозиторий (LDAP-база данных), или бизнес-служба, обращение к которой осуществляется при помощи протокола CORBA Internet Inter-ORB Protocol (IIOP) или низкоуровневых сокетов. Использующие DAO бизнес-компоненты работают с более простым интерфейсом, предоставляемым объектом DAO своим клиентам. DAO полностью скрывает детали реализации источника данных от клиентов. Поскольку при изменениях реализации источника данных представляемый DAO интерфейс не изменяется, этот паттерн дает возможность DAO принимать различные схемы хранилищ без влияния на клиенты или бизнес-компоненты. По существу, DAO выполняет функцию адаптера между компонентом и источником данных.
4.1 Структура
На рисунке 9.1 показана диаграмма классов, представляющая взаимоотношения в паттерне DAO.
Рисунок 9.1 Data Access Object
4.2 Участники и обязанности
На рисунке 9.2 представлена диаграмма последовательности действий, показывающая взаимодействия между различными участниками в данном паттерне.
Рисунок 9.2 Диаграмма последовательности действий паттерна Data Access Object
4.2.1 BusinessObject
BusinessObject представляет клиента данных. Это объект, который нуждается в доступе к источнику данных для получения и сохранения данных. BusinessObject может быть реализован как сессионный компонент, компонент управления данными или другой Java-объект, сервлет или вспомогательный компонент.
4.2.2 DataAccessObject
DataAccessObject является первичным объектом данного паттерна. DataAccessObject абстрагирует используемую реализацию доступа к данным для BusinessObject, обеспечивая прозрачный доступ к источнику данных. BusinessObject передает также ответственность за выполнение операций загрузки и сохранения данных объекту DataAccessObject.
4.2.3 DataSource
Представляет реализацию источника данных. Источником данных может быть база данных, например, RDBMS, OODBMS, XML-репозиторий, система плоских файлов и др. Источником данных может быть также другая система (традиционная/мэйнфрейм), служба (B2B-служба или система обслуживания кредитных карт), или какой-либо репозиторий (LDAP).
4.2.4 TransferObject
Представляет собой Transfer Object, используемый для передачи данных. DataAccessObject может использовать Transfer Object для возврата данных клиенту. DataAccessObject может также принимать данные от клиента в объекте Transfer Object для их обновления в источнике данных.
4.3 Стратегии
4.3.1 Стратегия Automatic DAO Code Generation
Поскольку BusinessObject соответствует конкретному DAO, есть возможность установить взаимоотношения между BusinessObject, DAO, и применяемыми реализациями (таблицы в RDBMS). После установления взаимоотношений появляется возможность написать простую утилиту для генерации кода, зависящую от приложения, которая может генерировать код для всех нужных приложению объектов DAO. Метаданные для генерации DAO могут определяться разработчиком в файле-дескрипторе. В качестве альтернативы генератор кода может автоматически проанализировать базу данных и предоставить необходимые для доступа к ней объекты DAO. Если требования к DAO являются достаточно сложными, можно использовать интсрументальные средства сторонних производителей, обеспечивающие отображение «объектный-реляционный» для баз данных RDBMS. Такие средства обычно имеют GUI-интерфейс для отображения бизнес-объектов в объекты персистентного хранилища и определяют промежуточные объекты DAO. Инструментальные средства автоматически генерируют код после завершения отображения и могут предоставлять другие ценные функции, например, кэширование результатов, кэширование запросов, интеграция с серверами приложений, интеграция с другими сторонними продуктами (например, распределенное кэширование) и т.д.
4.3.2 Стратегия Factory for Data Access Objects
Паттерн DAO может быть сделан очень гибким при использовании паттернов Abstract Factory [GoF] и Factory Method [GoF] (см. секцию «Связанные паттерны» в этом разделе).
Данная стратегия может быть реализована с использованием паттерна Factory Method для генерации нескольких объектов DAO, которые нужны приложению, в тех случаях, когда применяемое хранилище данных не изменяется при переходе от одной реализации к другой. Диаграмма классов этого случая приведена на рисунке 9.3.
Рисунок 9.3 Стратегия Factory for Data Access Object, использующая Factory Method
Когда используемое хранилище данных может измениться при переходе от одной реализации к другой, данная стратегия может быть реализована с применением паттерна Abstract Factory. Abstract Factory, в свою очередь, может создать и использовать реализацию Factory Method implementation, как рекомендуется в книге «Паттерны проектирования: Элементы повторно используемого объектно-ориентированного программного обеспечения» [GoF]. В этом случае данная стратегия предоставляет абстрактный объект генератора DAO (Abstract Factory), который может создавать конкретные генераторы DAO различного типа, причем каждый генератор может поддерживать различные типы реализаций персистентных хранилищ данных. После получения конкретного генератора для конкретной реализации вы можете использовать его для генерации объектов DAO, поддерживаемых и реализуемых в этой реализации.
Диаграмма классов этой стратегии представлена на рисунке 9.4. Эта диаграмма классов показывает базовый генератор DAO, являющийся абстрактным классом, который наследуется и реализуется различными конкретными генераторами DAO для поддержки доступа к специфической реализации хранилища данных. Клиент может получить реализацию конкретного генератора DAO, например RdbDAOFactory, и использовать его для получения конкретных объектов DAO, работающих с этой конкретной реализацией хранилища данных. Например, клиент может получить RdbDAOFactory и использовать его для получения конкретных DAO, таких как RdbCustomerDAO, RdbAccountDAO и др. Объекты DAO могут расширять и реализовывать общий базовый класс (показанные как DAO1 и DAO2) и детально описывать требования к DAO для поддерживаемых бизнес-объектов. Каждый конкретный объект DAO отвечает за соединение с источником данных и за получение и управление данными для поддерживаемого им бизнес-объекта.
Пример реализации паттерна DAO и его стратегий приведен в секции «Пример» данного раздела.
Рисунок 9.4 Стратегия Factory for Data Access Object, использующая Abstract Factory
Диаграмма последовательности действий, описывающая взаимодействия для этой стратегии, представлена на рисунке 9.5.
Рисунок 9.5 Диаграмма последовательности действий для стратегии Factory for Data Access Objects, использующей Abstract Factory
5 Выводы
Бизнес-объекты могут использовать источник данных, не имея знаний о конкретных деталях его реализации. Доступ является прозрачным, поскольку детали реализации скрыты внутри DAO.
Уровень объектов DAO облегчает приложению миграцию на другую реализацию базы данных. Бизнес-объекты не знают о деталях реализации используемых данных. Следовательно, процесс миграции требует изменений только в уровне DAO. Более того, при использовании стратегии генератора можно предоставить конкретную реализацию генератора для каждой реализации хранилища данных. В этом случае миграция на другую реализацию хранилища означает предоставление приложению новой реализации генератора.
Уменьшает сложность кода в бизнес-объектах
Поскольку объекты DAO управляют всеми сложностями доступа к данным, упрощается код бизнес-компонентов и других клиентов данных, использующих DAO. Весь зависящий от реализации код (например, SQL-команды) содержится в DAO, а не в бизнес-объекте. Это улучшает читаемость кода и производительность разработки.
Централизует весь доступ к данным в отдельном уровне
Поскольку все операции доступа к данным реализованы в объектах DAO, отдельный уровень доступа к данным может рассматриваться как уровень, изолирующий остальную часть приложения от реализации доступа к данным. Такая централизация облегчает поддержку и управление приложением.
Бесполезна для управляемой контейнером персистенции
Добавляет дополнительный уровень
Объекты DAO создают дополнительный уровень объектов между клиентом данных и источником данных, который должен быть разработан и реализован для использования преимуществ, предлагаемых данным паттерном. Но за реализуемые при этом преимущества приходится платить дополнительными усилиями при разработке.
Требуется разработка иерархии классов
При использовании стратегии генератора необходимо разработать и реализовать иерархию конкретных генераторов и иерархию конкретных объектов, производимых генераторами. Эти дополнительные усилия необходимо принимать во внимание, если существует достаточно оснований для реализации такой гибкости. Это увеличивает сложность разработки. Однако, вы можете сначала реализовать эту стратегию с паттерном Factory Method, а затем, при необходимости, перейти к паттерну Abstract Factory.
6 Примеры
6.1 Реализация паттерна Data Access Object
Код, использующий DAO, показан в примере 9.6. Диаграмма классов этого примера приведена на рисунке 9.6.
Рисунок 9.6 Реализация паттерна DAO
6.2 Реализация стратегии Factory for Data Access Objects
6.2.1 Использование паттерна Factory Method
Рассмотрим пример, в котором мы применяем данную стратегию для создания генератором многих объектов DAO для одной реализации базы данных (например, Oracle). Генератор производит такие объекты DAO, как CustomerDAO, AccountDAO, OrderDAO и др. Диаграмма классов для этого примера приведена на рисунке 9.7.
Рисунок 9.7 Реализация стратегии Factory for DAO, использующей Factory Method
Код генератора DAO (CloudscapeDAOFactory) приведен в примере 9.2.
6.2.2 Использование паттерна Abstract Factory
Рассмотрим пример, в котором мы применяем данную стратегию для трех различных баз данных. В этом случае может быть использован паттерн Abstract Factory. Диаграмма классов этого примера показана на рисунке 9.8. Код в примере 9.1 показывает фрагмент абстрактного класса DAOFactory. Этот генератор производит такие объекты DAO как CustomerDAO, AccountDAO, OrderDAO и др. Данная стратегия использует реализацию Factory Method в генераторах, созданных при помощи Abstract Factory.
Рисунок 9.8 Реализация стратегии Factory for DAO, использующей Abstract Factory
6.2.3 Пример 9.1 Абстрактный класс DAOFactory
// Абстрактный класс DAO Factory
public abstract class DAOFactory <
// Список типов DAO, поддерживаемых генератором
public static final int CLOUDSCAPE = 1 ;
public static final int ORACLE = 2 ;
public static final int SYBASE = 3 ;
.
// Здесь будет метод для каждого DAO, который может быть
// создан. Реализовывать эти методы
// должны конкретные генераторы.
public abstract CustomerDAO getCustomerDAO () ;
public abstract AccountDAO getAccountDAO () ;
public abstract OrderDAO getOrderDAO () ;
.
public static DAOFactory getDAOFactory (
int whichFactory ) <
switch ( whichFactory ) <
case CLOUDSCAPE:
return new CloudscapeDAOFactory () ;
case ORACLE :
return new OracleDAOFactory () ;
case SYBASE :
return new SybaseDAOFactory () ;
.
default :
return null ;
>
>
>
Код CloudscapeDAOFactory приведен в примере 9.2. Реализации OracleDAOFactory и SybaseDAOFactory аналогичны, за исключением особенностей каждой реализации, таких как JDBC-драйвер, URL базы данных и различий в синтаксисе SQL, если таковые имеются.
6.2.4 Пример 9.2 Конкретная реализация DAOFactory для Cloudscape
// Конкретная реализация DAOFactory для Cloudscape
import java.sql.*;
public class CloudscapeDAOFactory extends DAOFactory <
public static final String DRIVER=
«COM.cloudscape.core.RmiJdbcDriver» ;
public static final String DBURL=
«jdbc:cloudscape:rmi://localhost:1099/CoreJ2EEDB» ;
// метод для создания соединений к Cloudscape
public static Connection createConnection () <
// Использовать DRIVER и DBURL для создания соединения
// Рекомендовать реализацию/использование пула соединений
>
public CustomerDAO getCustomerDAO () <
// CloudscapeCustomerDAO реализует CustomerDAO
return new CloudscapeCustomerDAO () ;
>
public AccountDAO getAccountDAO () <
// CloudscapeAccountDAO реализует AccountDAO
return new CloudscapeAccountDAO () ;
>
public OrderDAO getOrderDAO () <
// CloudscapeOrderDAO реализует OrderDAO
return new CloudscapeOrderDAO () ;
>
.
>
Интерфейс CustomerDAO, показанный в примере 9.3, определяет DAO-методы для персистентного объекта Customer, которые реализованы всеми конкретными реализациями DAO, такими как, CloudscapeCustomerDAO, OracleCustomerDAO и SybaseCustomerDAO. Аналогично (но здесь не приводится) реализуются интерфейсы AccountDAO и OrderDAO, определяющие DAO-методы соответственно для бизнес-объектов Account и Order.
6.2.5 Пример 9.3 Базовый DAO-интерфейс для Customer
6.2.6 Пример 9.4 Реализация Cloudscape DAO для Customer
// Реализация CloudscapeCustomerDAO
// интерфейса CustomerDAO. Этот класс может содержать весь
// специфичный для Cloudscape код и SQL-команды.
// Клиент, таким образом, защищается от необходимости
// знать эти детали реализации.
public class CloudscapeCustomerDAO implements
CustomerDAO <
public CloudscapeCustomerDAO () <
// инициализация
>
// Следующие методы по необходимости могут использовать
// CloudscapeDAOFactory.createConnection()
// для получения соединения
Класс Customer Transfer Object показан в примере 9.5. Он используется объектами DAO для передачи и приема данных от клиентов. Использование объектов Transfer Object детально рассматривалось в паттерне Transfer Object.
6.2.7 Пример 9.5 Customer Transfer Object
public class Customer implements java.io.Serializable <
// переменные-члены
int CustomerNumber;
String name;
String streetAddress;
String city;
.
// методы getter и setter.
.
>
В примере 9.6 показано использование генератора DAO и DAO. Если реализация меняется от Cloudscape к другому продукту, необходимо изменить только вызов метода getDAOFactory() в генераторе DAO для получения другого генератора.
.
// создать требуемый генератор DAO
DAOFactory cloudscapeFactory =
DAOFactory.getDAOFactory ( DAOFactory.DAOCLOUDSCAPE ) ;
// Создать DAO
CustomerDAO custDAO =
cloudscapeFactory.getCustomerDAO () ;
7 Связанные паттерны
DAO использует Transfer Objects для передачи данных клиентам и от них.
Factory Method [GoF] и Abstract Factory [GoF]
Стратегия Factory for Data Access Objects использует паттерн Factory Method для реализации конкретных генераторов и их продуктов (объектов DAO). Для дополнительной гибкости, в стратегиях может быть применен паттерн Abstract Factory, как рассматривалось выше.
Паттерн DAO связан с паттерном Broker, который описывает подходы для разделения клиентов и серверов в распределенных системах. Паттерн DAO применяет этот паттерн более конкретно для разделения уровня ресурсов от клиентов и перемещения его в другой уровень, такой как бизнес-уровень, или уровень представлений.