Data Access Object (DAO). Уровень класса
При проектировании информационной системы выявляются некоторые слои, которые отвечают за взаимодействие различных модулей системы. Соединение с базой данных является одной из важнейшей составляющей приложения. Всегда выделяется часть кода, модуль, отвечающающий за передачу запросов в БД и обработку полученных от неё ответов. В общем случае, определение Data Access Object описывает его как прослойку между БД и системой. DAO абстрагирует сущности системы и делает их отображение на БД, определяет общие методы использования соединения, его получение, закрытие и (или) возвращение в Connection Pool.
Вершиной иерархии DAO является абстрактный класс или интерфейс с описанием общих методов, которые будут использоваться при взаимодействии с базой данных. Как правило, это методы поиска, удаление по ключу, обновление и т.д.
Набор методов не является завершённым, он зависит от конкретной системы. Фиктивный тип K является ключом сущности, редкая таблица, описывающая сущность, не имеет первичного ключа. Так же, в данном классе будет логичным разместить метод закрытие экземпляра PrepareStatement.
Уровень класса
Реализация DAO на уровне класса подразумевает использование одного единственного коннекта для вызова более чем одного метода унаследованного DAO класса. В этом случае, в вершине иерархии DAO AbstractController, в качестве поля объявляется connection. Абстрактный класс будет выглядеть следующим образом.
Экземпляр Connection доступен методу getPrepareStatement(String sql), который в свою очередь доступен любому методу конкретного DAO класса. Стоит помнить, что следует закрывать экземпляр PrepareStatement сразу после его отработки в блоках finally, а возвращать соединение в пул returnConnectionInPool() в части логики системы, где был вызван метод.
Шаблон 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.
Использование паттерна 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 и точнее настраивать реализацию работы с БД (без изменений в других модулях) или полностью заменить одну реализацию на другую.
Паттерн 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 применяет этот паттерн более конкретно для разделения уровня ресурсов от клиентов и перемещения его в другой уровень, такой как бизнес-уровень, или уровень представлений.











