Паттерн dao: Паттерн Data Access Object — JavaTutor.net

Содержание

Паттерн Data Access Object — JavaTutor.net

1 Контекст
2 Проблема
3 Ограничения
4 Решение
     4.1 Структура
     4.2 Участники и обязанности
         4.2.1 BusinessObject
         4.2.2 DataAccessObject
         4.2.3 DataSource
         4.2.4 TransferObject
     4.3 Стратегии
         4.3.1 Стратегия Automatic DAO Code Generation
         4.3.2 Стратегия Factory for Data Access Objects
5 Выводы
6 Примеры
     6.1 Реализация паттерна Data Access Object
     6.2 Реализация стратегии Factory for Data Access Objects
         6.2.1 Использование паттерна Factory Method
         6.2.2 Использование паттерна Abstract Factory
         6.2.3 Пример 9.1 Абстрактный класс DAOFactory
         6.2.4 Пример 9.2 Конкретная реализация DAOFactory для Cloudscape
         6.2.5 Пример 9.3 Базовый DAO-интерфейс для Customer
         6.2.6 Пример 9.4 Реализация Cloudscape DAO для Customer
         6.

2.7 Пример 9.5 Customer Transfer Object
         6.2.8 Пример 9.6 Использование DAO и DAO-генератора — код клиента
7 Связанные паттерны

1 Контекст

Способ доступа к данным бывает разным и зависит от источника данных. Способ доступа к персистентному хранилищу, например к базе данных, очень зависит от типа этого хранилища (реляционные базы данных, объектно-ориентированные базы данных, однородные или «плоские» файлы и т.д.) и от конкретной реализации.

2 Проблема

Многие реальные приложения платформы Java 2 Platform, Enterprise Edition (J2EE) должны использовать на некотором этапе персистентные данные. Для этих приложений персистентное хранение реализуется различными механизмами и существуют значительные отличия в API, используемых для доступа к этим механизмам. Другим приложениям может понадобиться доступ к данным, расположенным на разных системах. Например, данные могут находиться на мэйнфреймах, LDAP-репозиториях (Lightweight Directory Access Protocol — облегченный протокол доступа к каталогам) и т.

д. Другим примером является ситуация, когда данные предоставляются службами, выполняющимися на разных внешних системах, таких как системы business-to-business (B2B), системы обслуживания кредитных карт и др.

Обычно приложения совместно используют распределенные компоненты для представления персистентных данных, например, компоненты управления данными. Считается, что приложение использует управляемую компонентом персистенцию (BMP- bean-managed persistence) для своих компонентов управления данными, если эти компоненты явно обращаются к персистентным данным — то есть компонент содержит код прямого доступа к хранилищу данных. Приложение с более простыми требованиями может вместо компонентов управления данными использовать сессионные компоненты или сервлеты с прямым доступом к хранилищу данных для извлечения и изменения данных. Также, приложение могло бы использовать компоненты управления данными с управляемой контейнером персистенцией, передавая, таким образом, контейнеру функции управления транзакциями и деталями персистенции.

Для доступа к данным, расположенным в системе управления реляционными базами данных (RDBMS), приложения могут использовать JDBC API. JDBC API предоставляет стандартный механизм доступа и управления данными в персистентном хранилище, таком как реляционная база данных. JDBC API позволяет в J2EE-приложениях использовать SQL-команды, являющиеся стандартным средством доступа к RDBMS-таблицам. Однако, даже внутри среды RDBMS фактический синтаксис и формат SQL-команд может сильно зависеть от конкретной базы данных.

Для различных типов персистентных хранилищ существует еще большее число вариантов. Механизмы доступа, поддерживаемые API и функции отличаются для различных типов персистентных хранилищ, таких как RDBMS, объектно-ориентированные базы данных, плоские файлы и т.д. Приложения, которым нужен доступ к данным, расположенным на традиционных или несовместимых системах (например, мэйнфреймы или B2B-службы), часто вынуждены использовать патентованные API. Такие источники данных представляют проблему для приложений и могут потенциально создавать прямую зависимость между кодом приложения и кодом доступа к данным.

Когда бизнес-компонентам (компонентам управления данными, сессионным компонентам и даже презентационным компонентам, таким как сервлеты и вспомогательные объекты для JSP-страниц) необходим доступ к источнику данных, они могут использовать соответствующий API для получения соединения и управления этим источником данных. Но включение кода для установления соединения и доступа к данным в код этих компонентов создает тесную связь между компонентами и реализацией источника данных. Такая зависимость кода в компонентах может сделать миграцию приложения от одного типа источника данных к другому трудной и громоздкой. При изменениях источника данных компоненты необходимо изменить.

3 Ограничения

  • Компоненты управления данными с управляемой компонентом персистенцией, сессионные компоненты, сервлеты и другие объекты, такие как вспомогательные объекты для JSP-страниц, должны получать и сохранять информацию в персистентных хранилищах и других источниках данных, например традиционных системах, B2B, LDAP и т.

    д.

  • API доступа к персистентному хранилищу данных может зависеть от поставщика продукта. Другие источники данных могут иметь нестандартные или патентованные API. Эти API и их возможности зависят от типа хранилища данных — RDBMS, система управления объектно-ориентированными базами данных (OODBMS), XML-документы, плоские файлы и т.д. Унифицированный API доступа к этим несовместимым системам отсутствует.

  • Для извлечения или сохранения данных во внешних и/или традиционных системах компоненты обычно используют патентованные 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.

4.2 Участники и обязанности

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

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.5.

5 Выводы

  • Разрешает прозрачность

    Бизнес-объекты могут использовать источник данных, не имея знаний о конкретных деталях его реализации. Доступ является прозрачным, поскольку детали реализации скрыты внутри DAO.

  • Облегчает миграцию

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

  • Уменьшает сложность кода в бизнес-объектах

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

  • Централизует весь доступ к данным в отдельном уровне

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

  • Бесполезна для управляемой контейнером персистенции

    Поскольку EJB-контейнер управляет компонентами с управляемой контейнером персистенцией (CMP — container-managed persistence), контейнер автоматически обслуживает весь доступ к хранилищу данных. Приложения, использующие компоненты управления данными этого типа, не нуждаются в уровне объектов DAO, поскольку сервер приложений обеспечивает эту функциональность. Однако, объекты DAO остаются полезны в случаях, когда необходимо использовать комбинацию CMP (для компонентов управления данными) и BMP (для сессионных компонентов, сервлетов).

  • Добавляет дополнительный уровень

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

  • Требуется разработка иерархии классов

    При использовании стратегии генератора необходимо разработать и реализовать иерархию конкретных генераторов и иерархию конкретных объектов, производимых генераторами. Эти дополнительные усилия необходимо принимать во внимание, если существует достаточно оснований для реализации такой гибкости. Это увеличивает сложность разработки. Однако, вы можете сначала реализовать эту стратегию с паттерном Factory Method, а затем, при необходимости, перейти к паттерну Abstract Factory.

6 Примеры

6.1 Реализация паттерна Data Access Object

Код объекта DAO для персистентного объекта, предоставляющего информацию о клиенте (Customer), представлен в примере 9.4. CloudscapeCustomerDAO создает объект Customer Transfer Object при вызове метода findCustomer().

Код, использующий DAO, показан в примере 9.6. Диаграмма классов этого примера приведена на рисунке 9.6.

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

// Интерфейс, который должны поддерживать все CustomerDAO
public interface CustomerDAO {
  public int insertCustomer(...);
  public boolean deleteCustomer(...);
  public Customer findCustomer(...);
  public boolean updateCustomer(...);
  public RowSet selectCustomersRS(...);
  public Collection selectCustomersTO(...);
  ...
}

CloudscapeCustomerDAO реализует CustomerDAO, как показаго в примере 9.4. Реализации других объектов DAO, таких как CloudscapeAccountDAO, CloudscapeOrderDAO, OracleCustomerDAO, OracleAccountDAO, и т. д. — аналогичны.

6.2.6 Пример 9.4 Реализация Cloudscape DAO для Customer
// Реализация CloudscapeCustomerDAO
// интерфейса CustomerDAO. Этот класс может содержать весь
// специфичный для Cloudscape код и SQL-команды.
// Клиент, таким образом, защищается от необходимости
// знать эти детали реализации.import java.sql.*;public class CloudscapeCustomerDAO implements
    CustomerDAO {
 
  public CloudscapeCustomerDAO() {
    // инициализация
  }

 

// Следующие методы по необходимости могут использовать
  // CloudscapeDAOFactory.createConnection()
  // для получения соединения

 

public int insertCustomer(...) {
    // Реализовать здесь операцию добавления клиента.
    // Возвратить номер созданного клиента
    // или -1 при ошибке
  }
 
  public boolean deleteCustomer(...) {
    // Реализовать здесь операцию удаления клиента.
    // Возвратить true при успешном выполнении, false при ошибке
  }

 

public Customer findCustomer(. ..) {
    // Реализовать здесь операцию поиска клиента, используя
    // предоставленные значения аргументов в качестве критерия поиска.
    // Возвратить объект Transfer Object при успешном поиске,
    // null или ошибку, если клиент не найден.
  }

 

public boolean updateCustomer(...) {
    // Реализовать здесь операцию обновления записи,
    // используя данные из customerData Transfer Object
    // Возвратить true при успешном выполнении, false при ошибке
  }

 

public RowSet selectCustomersRS(...) {
    // Реализовать здесь операцию выбора клиентов,
    // используя предоставленный критерий.
    // Возвратить RowSet.
  }

 

public Collection selectCustomersTO(...) {
    // Реализовать здесь операцию выбора клиентов,
    // используя предоставленный критерий.
    // В качестве альтернативы, реализовать возврат
    // коллекции объектов Transfer Object.
  }
  ...
}

Класс 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 для получения другого генератора.

6.2.8 Пример 9.6 Использование DAO и DAO-генератора — код клиента
...
// создать требуемый генератор DAO
DAOFactory cloudscapeFactory =  
  DAOFactory.getDAOFactory(DAOFactory.DAOCLOUDSCAPE);// Создать DAO
CustomerDAO custDAO =
  cloudscapeFactory.getCustomerDAO();// создать нового клиента
int newCustNo = custDAO. insertCustomer(...);// найти объект customer. Получить объект Transfer Object.
Customer cust = custDAO.findCustomer(...);// изменить значения в Transfer Object.
cust.setAddress(...);
cust.setEmail(...);
// обновить объект customer, используя DAO
custDAO.updateCustomer(cust);// удалить объект customer
custDAO.deleteCustomer(...);
// выбрать всех клиентов одного города
Customer criteria=new Customer();
criteria.setCity("New York");
Collection customersList =
  custDAO.selectCustomersTO(criteria);
// возвратить customersList - коллекцию объектов Customer
// Transfer Objects. Проходит по коллекции для
// получения значений....

7 Связанные паттерны

  • Transfer Object

    DAO использует Transfer Objects для передачи данных клиентам и от них.

  • Factory Method [GoF] и Abstract Factory [GoF]

    Стратегия Factory for Data Access Objects использует паттерн Factory Method для реализации конкретных генераторов и их продуктов (объектов DAO). Для дополнительной гибкости, в стратегиях может быть применен паттерн Abstract Factory, как рассматривалось выше.

  • Broker [POSA1]

    Паттерн DAO связан с паттерном Broker, который описывает подходы для разделения клиентов и серверов в распределенных системах. Паттерн DAO применяет этот паттерн более конкретно для разделения уровня ресурсов от клиентов и перемещения его в другой уровень, такой как бизнес-уровень, или уровень представлений.

Шаблон DAO примеры, достоинства и недостатки Проектирование…

Привет, Вы узнаете про dao, Разберем основные ее виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое dao,шаблон dao,паттерн dao , настоятельно рекомендую прочитать все из категории Проектирование веб сайта или программного обеспечения

1. Обзор

Шаблон Data Access Object (DAO) является структурным шаблоном, который позволяет нам изолировать прикладной/бизнес-уровень от постоянного уровня (обычно это реляционная база данных , но это может быть любой другой постоянный механизм) с использованием абстрактного API .

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

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

2. Простая реализация

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

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

2.1. Домен Класс

Поскольку наше приложение будет работать с пользователями, нам нужно определить только один класс для реализации его доменной модели:


 

Класс User представляет собой простой контейнер для пользовательских данных, поэтому он не реализует никакого другого поведения, заслуживающего внимания.

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

Ну, это именно та проблема, которую пытается решить шаблон DAO.

2.2. DAO API

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

Вот API DAO:


 

С высоты птичьего полета ясно, что интерфейс Dao определяет абстрактный API, который выполняет операции CRUD над объектами типа T .

Благодаря высокому уровню абстракции, который обеспечивает интерфейс, легко создать конкретную, детальную реализацию, которая работает с объектами User .

2.3. UserDao Класс

Давайте определим пользовательскую реализацию интерфейса Dao :


 

Класс UserDao реализует все функции, необходимые для извлечения, обновления и удаления объектов User .

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

Хотя оба класса User и UserDao сосуществуют независимо в одном и том же приложении, нам все еще необходимо выяснить, как последний может использоваться для сохранения уровня персистентности скрытым от логики приложения:


 

Пример придуман, но в двух словах показывает мотивы, лежащие в основе шаблона DAO . Об этом говорит сайт https://intellect.icu . В этом случае метод main просто использует экземпляр UserDao для выполнения операций CRUD над несколькими объектами User .

  • Наиболее значимым аспектом этого процесса является то, как UserDao скрывает от приложения все низкоуровневые сведения о том, как объекты сохраняются, обновляются и удаляются ** .

3. Использование шаблона с JPA

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

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

3.1. JpaUserDao Класс

С учетом вышесказанного давайте создадим новую реализацию интерфейса Dao , чтобы мы могли увидеть, как он может инкапсулировать функциональность, предоставляемую менеджером сущностей JPA из коробки:


 

Кроме того, если мы внимательно посмотрим на класс, мы поймем, как использовать https://en.wikipedia.org/wiki/Composition over inheritance[Composition]и Dependency Инъекция позволяет нам вызывать только методы менеджера сущностей, требуемые нашим приложением.

Проще говоря, у нас есть специализированный API для конкретного домена, а не весь API менеджера сущностей.

3.2. Рефакторинг класса User

В этом случае мы будем использовать Hibernate в качестве реализации по умолчанию для JPA, поэтому мы соответствующим образом проведем рефакторинг класса User :


 

3. 3. Начальная загрузка JPA Entity Manager программно

Предполагая, что у нас уже есть работающий экземпляр MySQL, работающий локально или удаленно, и таблица базы данных «пользователи» , заполненная некоторыми записями пользователей, нам нужно получить диспетчер сущностей JPA, чтобы мы могли использовать класс JpaUserDao для выполнения операций CRUD в базе данных.

В большинстве случаев мы выполняем это с помощью типичного файла «persistence.xml», который является стандартным подходом

В этом случае мы воспользуемся подходом xml-less и получим менеджер сущностей с простой Java через удобный https://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/ . jpa/boot/internal/EntityManagerFactoryBuilderImpl.html[EntityManagerFactoryBuilderImpl] класс.

Для получения подробного объяснения о том, как запустить реализацию JPA с помощью Java, перейдите по ссылке:/java-bootstrap-jpa[эта статья].

3.4. UserApplication Class

Наконец, давайте проведем рефакторинг исходного класса UserApplication , чтобы он мог работать с экземпляром JpaUserDao и выполнять операции CRUD с сущностями User :


  

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

В большинстве приложений есть структура DI, которая отвечает за внедрение экземпляра JpaUserDao в класс UserApplication . Для простоты мы опустили детали этого процесса.

Наиболее важный момент, на который следует обратить внимание, заключается в том, как класс JpaUserDao помогает полностью исключить независимость класса UserApplication от того, как уровень персистентности выполняет операции CRUD ** .

Кроме того, мы могли бы поменять MySQL на любую другую СУБД (и даже на плоскую базу данных) в будущем, и все же наше приложение продолжало бы работать как положено, благодаря уровню абстракции, обеспечиваемому интерфейсом Dao и менеджером сущностей ,

Преимущества использования шаблона проектирования DAO

Шаблон проектирования DAO или Data Access Object — хороший пример объектно-ориентированных принципов абстракции и инкапсуляции. Он отделяет логику сохраняемости от отдельного уровня, называемого уровнем доступа к данным, который позволяет приложению безопасно реагировать на изменения в механизме сохраняемости. Например, если вы перейдете от механизма сохранения на основе файлов к базе данных, ваше изменение будет ограничено уровнем доступа к данным и не повлияет на уровень службы или объекты домена. Объект доступа к данным или шаблон DAO в значительной степени стандартен в приложении Java, являясь ядром Java, веб-приложением или корпоративным приложением. Ниже приведены еще несколько преимуществ использования шаблона DAO в приложении Java:

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

  2. Шаблон проектирования DAO позволяет тесту JUnit работать быстрее, поскольку он позволяет создавать Mock и избегать подключения к базе данных для запуска тестов. Это улучшает тестирование, потому что проще написать тест с Mock-объектами, а не тест интеграции с базой данных. В случае возникновения каких-либо проблем при запуске модульного теста вам нужно проверять только код, а не базу данных. Также защищает от проблем с подключением к базе данных и окружающей среды.

  3. Поскольку шаблон DAO основан на интерфейсе, он также продвигает принцип объектно-ориентированного проектирования «программирование для интерфейса, а не реализация», что приводит к гибкому и качественному коду.

  4. Сила шаблона DAO состоит в том, что они позволяют вам создать хороший уровень абстракции реальной системы хранения. Они обеспечивают более объектно-ориентированное представление уровня сохраняемости и четкое разделение между доменом и кодом, который фактически будет выполнять доступ к данным (прямой JDBC, структуры сохраняемости, ORM или даже JPA).

  5. Общие вызовы для получения объектов.
  6. После того, как вы настроили общий поток создания / чтения / обновления / удаления, общий макет можно повторить для других DAO.
  7. Он также консолидирует, где может идти конкретная часть вашего кода. Отделяет бизнес-логику от других компонентов вашего кода.
  8. абстрактное разделение
  9. единая точка определения для таблицы БД — Отображение атрибутов объекта
  10. прозрачная возможность реализации DAO для других типов хранилищ
  11. разработать шаблон интерфейса, которому следуют все DAO
  12. разработка более или менее стандартного тестового класса JUnit для результатов DAO в лучшем тестовом покрытии
  13. полный контроль над спецификой
  14. нет потери производительности из-за слишком общего решения
  15. Абстракция для реальной реализации доступа к базе данных отделяет стратегию доступа к данным от бизнес-логики пользователя. Это позволило нам выбрать краткосрочную стратегию реализации (шаблон Spring JDBC) для начальной фазы проекта с возможностью перехода на IBATIS или Hibernate позже. (Выбор, который мы не можем сделать в настоящее время. )
  16. Такое разделение дает значительные преимущества тестируемости, так как вся реализация доступа к данным может быть смоделирована в модульном тестировании. (Это, наверное, самое большое преимущество)
  17. Объединение этого со Spring позволяет нам внедрить любую реализацию БД в выбранную нами систему (хотя это, возможно, больше говорит о DI, чем о шаблоне DAO).

Недостатки использования шаблона DAO

  1. это еще один слой, который усложняет системы … Но я думаю, это цена, которую нужно заплатить за то, чтобы не привязывать свой код к базовому API сохраняемости.
  2. Это не самая гибкая вещь.
  3. Если вы хотите отложить загрузку некоторых дочерних объектов, вам придется либо смешивать DAO с другими слоями, либо принимать меры предосторожности при попытке получить ленивые объекты.
  4. Если вы напишете DAO от руки, код может стать утомительным и повторяющимся.
  5. много шаблонного кода

4. Заключение

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

См. также

На этом все! Теперь вы знаете все про dao, Помните, что это теперь будет проще использовать на практике. Надеюсь, что теперь ты понял что такое dao,шаблон dao,паттерн dao и для чего все это нужно, а если не понял, или есть замечания, то нестесняся пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Проектирование веб сайта или программного обеспечения

DAO vs ORM (hibernate) паттерн

ORM и DAO-ортогональные понятия. Один из них связан с тем, как объекты сопоставляются с таблицами базы данных, другой-это шаблон проектирования для записи объектов, которые обращаются к данным. Вы не выбираете ‘between’ из них. Вы можете иметь ORM и DAO-это одно и то же приложение, точно так же, как вам не нужно ORM, чтобы использовать шаблон DAO.

Тем не менее, хотя вам никогда ничего не нужно , вы должны использовать DAOs. Шаблон поддается модульному коду. Вы держите всю свою логику настойчивости в одном месте (разделение проблем, борьба с дырявыми абстракциями). Вы позволяете себе тестировать доступ к данным отдельно от rest приложения. И вы позволяете себе протестировать rest приложения, изолированного от доступа к данным (т. Е. Вы можете издеваться над своим DAOs).

Кроме того, следовать шаблону DAO легко, даже если реализация доступа к данным может быть затруднена. Таким образом, это стоит вам очень мало (или ничего), и вы получаете много.

EDIT — Что касается вашего примера, ваш метод входа в систему должен быть в некотором роде AuthenticationService. Вы можете обрабатывать исключения там (в методе входа в систему). Если бы вы использовали Spring, он мог бы управлять кучей вещей для вас: (1) транзакции, (2) внедрение зависимостей. Вам не нужно было бы писать свои собственные транзакции или фабрики dao, вы могли бы просто определить границы транзакций вокруг своих методов обслуживания и определить свои реализации DAO как бобы, а затем подключить их к своему сервису.

EDIT2

Основная причина использования шаблона заключается в разделении проблем. Это означает, что весь ваш код сохранения находится в одном месте. Побочным эффектом этого является возможность тестирования и ремонтопригодность, а также тот факт, что это облегчает последующее переключение реализаций. Если вы строите Hibernate на основе DAOs, вы можете абсолютно манипулировать сеансом в DAO, это то, что вы должны делать. Анти-шаблон-это когда код, связанный с сохраняемостью, происходит за пределами уровня сохраняемости (закон протекающих абстракций).

Транзакции немного сложнее. На первый взгляд может показаться, что транзакции связаны с постоянством, и это так. Но они связаны не только с настойчивостью. Транзакции также относятся к вашим услугам, поскольку ваши методы обслуживания должны определять «единицу работы», что означает, что все, что происходит в методе обслуживания, должно быть атомарным. Если вы используете hibernate транзакции, то вам придется написать код транзакции hibernate за пределами вашего DAOs, чтобы определить границы транзакций вокруг служб, использующих множество методов DAO.

Но обратите внимание, что транзакции могут быть независимыми от вашей реализации-вам нужны транзакции независимо от того, используете вы hibernate или нет. Также обратите внимание, что вам не нужно использовать механизм транзакций hibernate-вы можете использовать транзакции на основе контейнеров, транзакции JTA и т. Д.

Без сомнения, если вы не используете Spring или что-то подобное, транзакции будут болезненными. Я настоятельно рекомендую использовать Spring для управления вашими транзакциями или спецификацию EJB, в которой, как я полагаю , вы можете определять транзакции вокруг ваших сервисов с помощью аннотаций.

Проверьте следующие ссылки для транзакций на основе контейнеров.

Транзакции, управляемые контейнерами

Сеансы И Транзакции

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

Другая (менее элегантная) альтернатива состоит в том, чтобы поместить все атомарные единицы работы в DAOs. Вы могли бы иметь CRUD DAOs для простых операций, а затем более сложные DAOs, которые выполняют более одной операции CRUD. Таким образом, ваши программные транзакции останутся в DAO, а ваши службы будут вызывать более сложные DAOs, и вам не придется беспокоиться о транзакциях.

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

AO против ORM(hibernate) шаблон

(thanx @ daff )

Обратите внимание, как определение интерфейса делает так, что ваша бизнес-логика заботится только о поведении UserDao. Его не волнует реализация. Вы можете написать DAO, используя hibernate, или просто JDBC. Таким образом, вы можете изменить реализацию доступа к данным, не затрагивая rest вашей программы.

Блог сурового челябинского программиста: О спорном паттерне DAO

В последние годы, после выхода спецификации Java EE 6, среди разработчиков и архитекторов информационных систем развернулась нешуточная дискуссия на тему паттерна DAO. Некоторые архитекторы и евангелисты уверены, что данный паттерн устарел и является избыточным решением в эпоху инъектируемого сразу в EJB- или CDI-компоненты JPA EntityManager’а. Другие же упорно настраивают на необходимости его применения.
@Stateless
public class MyDocumentService implements DocumentService {

    @PersistenceContext
    private EntityManager em;

    // ...
}

Давайте попробуем разобраться в данном вопросе.
Паттерн DAO предназначен для отделения взаимодействия с хранилищем данных от бизнес-логики приложения. Т.е. вся логика, отвечающая за сохранение, изменение, извлечение сущностей выносится в отдельные DAO-классы, а код, инкапсулирующий бизнес-логику приложения (т.н. сервисы), взаимодействует с этими классами, а не непосредственно с хранилищем данных. Такой подход обеспечивает гибкость в выборе подсистемы хранения для приложения. Если необходимо перейти с использования ORM на прямое взаимодействие с базой данных посредством JDBC или отказаться от использования внутреннего хранилища данных для приложения и перейти на взаимодействие с системой управления мастер-данными посредством веб-сервисов, то достаточно только заменить реализацию DAO.

Все прекрасно и звучит красиво, но как известно бесплатного супа не бывает. Поддержка конструкции «интерфейс сервиса — сервис — интерфейс DAO — одна или несколько реализаций DAO» довольно утомительна. Чтобы добавить новую операцию в сервис, в общем случае необходимо сделать следующее:

  1. Добавить метод в интерфейс сервиса.
  2. Добавить метод в реализацию сервиса.
  3. Добавить метод в интерфейс DAO.
  4. Добавить метод в одну или несколько реализаций DAO.


При этом большинство методов сервисов будут иметь подобную реализацию:
public class MyDocumentService implements DocumentService {

    private DocumentDao documentDao;

    @Transational
    public Document getDocumentById(Long id) {
        return documentDao.getDocumentById(id);
    }

    //...
 
    @Transactional
    public void saveDocument(Document document) {
        documentDao.save(document);
    }
}

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

Но важно даже не это, в конце концов человек ко всему привыкает. Главное то, что выполнять их не зачем, прозрачной смены подсистемы хранения паттерн DAO на самом деле не предоставляет. Каждый рекламируемый вариант замены реализации DAO (переключение между ORM и JDBC, между реляционными СУБД и NoSQL, между СУБД и веб-сервисами и т.д.) имеет свои подводные камни и практически всегда требует внесения изменений в слой бизнес-логики.

1. Переключение между ORM и JDBC. ORM-фреймворки, в отличие от JDBC реализуют прозрачное хранение (т.н. transparent persistence). Если после загрузки объекта в сессию обратиться к его сеттерам, то после синхронизации сессии с базой данных эти изменения будут отражены в ней. При использовании же JDBC такой прозрачности нет, поэтому любые изменения необходимо явно синхронизировать с базой данных посредством вызова соответствующих методов DAO, а значит требуется добавить обращение к этим методами в слой бизнес-логики, т. к. ранее, при использовании ORM, они отсутствовали.

2. Переключение с традиционной реляционной СУБД на NoSQL-хранилище тоже не всегда можно сделать прозрачно. Большинство NoSQL-хранилищ не поддерживают транзакции в терминах ACID. Бизнес-логику, реализуемую в сервисах, придется адаптировать к нетранзакционному хранилищу. Например, для повышения быстродействия мы могли исключить некоторые проверки из бизнес-логики, надеясь, что в СУБД отработают ограничения целостности, а в случае их нарушения будет выброшено исключение и произойдет откат транзакции. Если же транзакции нет, то изменения, выполненные до момента выбрасывания исключения, сохранятся в БД, что приведет к нарушению целостности данных.

3. Аналогичные соображения можно высказать и относительно перехода с использования реляционной СУБД на взаимодействие с внешним хранилищем посредством веб-сервисов. Контекст транзакции по веб-сервисам без использования специальных техник, таких как WS-AtomicTransaction, не передается, а применение данных техник не всегда возможно, а там где возможно ведет к существенному снижению производительности.

4. Общее соображение. Давайте подумаем, что делать, если необходимо заменить слой хранения в сервисе частично. Предположим, что часть справочников будет подгружаться из системы управления мастер-данными (МДМ), а часть по прежнему храниться в СУБД. Нужно ли создавать композитные DAO-классы, в которых часть методов оперирует с МДМ, а остальные — с СУБД (т.е. по сути, произвести не расширение системы классов, а модификацию конкретного класса, тем самым нарушив принцип открытости-закрытости SOLID), или лучше инжектировать в сервис две реализации одного и того же DAO, затем три и т.д.?

Выводы

Радикальная смена подсистемы хранения только посредством использования паттерна DAO — миф. Как правило такая замена повлечет за собой и модификацию бизнес-логики. Максимум что можно сделать посредством данного паттерна — обеспечить переход на другую реализацию в рамках одной технологии. Например, сменить производителя и, соответственно, используемый диалект языка SQL в рамках технологии «реляционные СУБД». Т.е. перейти с Oracle на DB2 при использовании JDBC посредством паттерна DAO можно, а замена Oracle на Cassandra скорее всего потребует изменений и в слое бизнес-логики. Но заменить диалект СУБД при использовании ORM можно посредством изменения одной строчки в конфигурации. Более радикальные изменения потребуют модификации бизнес-логики и в случае использования структуры из «интерфейс сервиса — тело сервиса — интерфейс DAO — одна или несколько реализаций DAO«, и в случае простой инъекции Entity Manager‘а в сервис.

UPD: Статья вызвала некий резонанс. В обсуждении на форуме JavaTalks есть интересные мысли, но, как впрочем и ожидалось, дискуссия в конце концов свелась к переходу на личности и мнениям, что кто DAO не пишет, тому стоит перейти на другой язык программирования. Ситуация характерная для русскоязычной аудитории. Не служил — не мужик, че. Однако и в комментариях к данной заметке, и на форуме был высказан ряд соображений в пользу данного паттерна, достойных внимания.

  1. В приложении присутствует сложная логика доступа к данным и мы хотим эту логику тестировать. При этом хорошо, если у нас приложение переносимое между СУБД, тогда можем запускать тесты на какой-нибудь h3, если же приложение не переносимо, то придется запускать целевую СУБД, что во-первых, может быть медленне, а во-вторых, требует существенного усложнения окружения разработчика. В качестве контраргумента можно высказать следующее: если запросы инкапсулированы в одном месте приложения, например в мэпинге myBatis или вынесены в именованные запросы JPA, то их можно протестировать и без отдельного слоя DAO. Он будет нужен, если логика доступа к данным, не бизнес-логика, несколько сложнее, чем выполнение одного запроса на один метод, например, реализован поиск по нескольким источникам данных или шардинг, т.е. когда в банальном getById выполняется несколько запросов. Тогда в DAO действительно есть смысл.
  2. DAO облегчает модульное тестирование не слоя доступа к данным, а вышележащего слоя бизнес-логики. Можно сделать моки на объекты DAO и тестировать только логику. Если в проекте действительно используются модульные тесты бизнес-логики, то подход имеет право на жизнь.
  3. Если у заказчика много денег и он готов оплачивать отдельную команду, разрабатывающую слой DAO. Странная ситуация, но один товарищ на форуме отметил, что работает уже во второй компании, в которой процесс разработки построен таким образом. На мой взгляд это какой-то крайний случай следования паттернам. Более приближенной к реальности является ситуация, когда есть выделенные программисты СУБД, которые пишут слой доступа к данным на языке СУБД, а разработчики среднего слоя (мы с вами) просто вызывают соответствующие хранимые процедуры, представления, или отправляют подсказанные им запросы.
  4. Пример из моей практики. Интеграционное приложение, в котором логика одна: перелей из пустого в порожнее и залогируй, а типов данных много. Тогда действительно разумно сделать сервис с одним методом, принимающий на вход два объекта-DAO: один отвечает за извлечение данных данного типа из одной информационной системы, другой — за их сохранение в соответствующей информационной системе. Здесь мы получаем все преимущества, приписываемые паттерну DAO: и переносимость, только не между источниками данных, а между информационными потоками, и полиморфизм, когда несколько классов, каждый из которых работает со своим типом данных, реализуют обобщенный интерфейс, а сервис, инкапсулирующий в себе бизнес-логику, работает только с этим интерфейсом. Сами DAO я немного описывал в своих предыдущих заметках (раз и два). Но такой подход весьма нишевый, далеко не в каждом приложении одна логика выпролняется над несколькими типами данных.

UPD 13.11.2014: Сейчас на практике получаю опыт миграции приложения с одной реляционной СУБД на другую и одной и той же СУБД между аппаратно/программными платформами. Надо сказать, что поведение СУБД зачастую бывавет очень разным. Дилемму «версионник-блокировочник» знают все, но в данном случае различия тоньше. При этом использование даже такого развитого ORM, как Hibernate, неспособно помочь. Приходится использовать нативные запросы и привлекать опытных DBA. Это я пишу к тому, что даже если вы протестировали ваше DAO на какой-нибудь In-Memory СУБД, то далеко не факт, что на промышленной системе код будет работать. Но тесты будут зелеными, некоторым это греет душу, да.

Понравилось сообщение — подпишитесь на блог

Паттерны/шаблоны проектирования

Паттерны (или шаблоны) проектирования описывают типичные способы решения часто встречающихся проблем при проектировании программ.