Уровни DAO и Service (JPA/Hibernate + Spring)

Я разрабатываю новое приложение на основе JPA/Hibernate, Spring и Wicket. Различие между уровнями DAO и Service не так ясно для меня. Согласно Википедии, DAO

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

Мне было интересно, может ли DAO содержать методы, которые действительно не имеют особого отношения к доступу к данным, но проще ли выполнить запрос? Например, "получите список всех авиакомпаний, которые работают в определенном наборе аэропортов"? Мне кажется, что это скорее метод уровня сервиса, но я не уверен, что использование JPA EntityManager на уровне сервиса является примером хорошей практики?

Ответ 1

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

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

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

Ответы на конкретные вопросы:

Мне было интересно, может ли DAO содержат методы, которые на самом деле не имеют сделать многое с доступом к данным, но проще всего выполнить запрос?

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

Мне кажется, что я больше метод сервисного уровня, но я не уверен если используется JPA EntityManager в сервисный уровень является примером хорошего практика?

Если вы собираетесь использовать диспетчер сущностей в своей службе, тогда подумайте о менеджере сущностей как о вашем DAO, потому что это именно то, что есть. Если вам нужно удалить избыточное построение запросов, не делайте этого в своем классе обслуживания, извлеките его в класс, в котором используется диспетчер сущностей, и сделайте это DAO. Если ваш вариант использования действительно прост, вы можете полностью пропустить уровень сервиса и использовать диспетчер сущностей или DAO в контроллерах, потому что все ваши службы будут выполнять передачу вызовов на getAirplaneById() в DAO findAirplaneById()

ОБНОВЛЕНИЕ. Чтобы уточнить, что касается обсуждения ниже, использование диспетчера сущностей в службе, вероятно, не является лучшим решением в большинстве ситуаций, где также есть уровень DAO по разным причинам, выделенным в комментариях. Но, на мой взгляд, это было бы вполне разумно:

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

Пример.

//some system that contains all our customers information
class PersonDao {
   findPersonBySSN( long ssn )
}

//some other system where we store pets
class PetDao {
   findPetsByAreaCode()
   findCatByFullName()
}

//some web portal your building has this service
class OurPortalPetLostAndFoundService {

   notifyOfLocalLostPets( Person p ) {
      Location l = ourPortalEntityManager.findSingle( PortalUser.class, p.getSSN() )
        .getOptions().getLocation();
      ... use other DAO to get contact information and pets...
   }
}

Ответ 2

Ясно одно: если вы используете EntityManager на уровне сервиса, вам не нужен уровень dao (только один уровень должен знать подробности реализации). Кроме того, существуют разные мнения:

  • Некоторые говорят, что EntityManager предоставляет все необходимые функции dao, поэтому они внедрить EntityManager в службу слой.
  • Другие имеют традиционный уровень dao поддерживаемые интерфейсами (поэтому служба слой не привязан к реализации подробности).

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

Я бы сказал, если у вас небольшой проект, используйте JPA на уровне сервиса, но в большом проекте используйте выделенный слой DAO.

Ответ 4

Традиционно вы должны писать интерфейсы, которые определяют контракт между вашим уровнем сервиса и уровнем данных. Затем вы записываете реализации, и это ваши DAO.

Вернемся к вашему примеру. Предполагая, что отношения между аэропортом и авиакомпанией много для многих с таблицей, содержащей airport_id и авиакомпанией_ид, у вас может быть интерфейс;

public interface AirportDAO
{
   public List<Airline> getAirlinesOperatingFrom(Set<Airport> airports);
}

.. и вы можете обеспечить реализацию Hibernate,

public class HibernateAirportDAO implements AirportDAO
{
   public List<Airline> getAirlinesOperatingFrom(Set<Airport> airports)
   {
      //implementation here using EntityManager.
   }
}

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

Вы также можете посмотреть шаблон Abstract Factory для написания фабрик DAO. Например:

public abstract class DAOFactory
{
   private static HibernateDAOFactory hdf = new HibernateDAOFactory();

   public abstract AirportDAO getAirlineDAO();

   public static DAOFactory getFactory()
   {
      //return a concrete implementation here, which implementation you
      //return might depend on some application configuration settings.
   }
}

public class HibernateDAOFactory extends DAOFactory
{
   private static EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("myPersistenceUnit");

   public static EntityManager getEM()
   {
      return emFactory.createEntityManager();
   }

   public AirportDAO getAirportDAO()
   {
      return new HibernateAirportDAO();
   }
}

Этот шаблон позволяет вашему HibernateDAOFactory иметь одну EMF и поставлять отдельные экземпляры DAO с EM. Если вы не хотите идти по дальнему маршруту, то Spring отлично подходит для обработки экземпляров DAO для вас с инъекцией зависимости.

Изменить: Уточнено несколько предположений.

Ответ 5

Дао - объект доступа к данным. Он сохраняет/обновляет/выбирает объекты в базе данных. Для этого используется объект менеджера сущностей (по крайней мере, в open jpa). Вы также можете запустить запрос с этим менеджером сущностей. Это не sql, а JPQL (язык запросов на сохранение Java).

Простой пример:

emf = Persistence.createEntityManagerFactory("localDB");
em = emf.createEntityManager();

Query q = em.createQuery("select u from Users as u where u.username = :username", Users.class);
q.setParameter("username", username);

List<Users> results = q.getResultList();

em.close();
emf.close();