Автор оригинала: Eugen Paraschiv.
1. Обзор
Эта статья будет посвящена упрощению уровня DAO путем использования единого обобщенного объекта доступа к данным для всех сущностей в системе, что приведет к элегантному доступу к данным без ненужного беспорядка или многословия.
Мы будем опираться на абстрактный класс DAO, который мы видели в нашей предыдущей статье о Spring и Hibernate, и добавим поддержку дженериков.
Дальнейшее чтение:
Введение в Spring Data JPA
Руководство по JPA с пружиной
Аудит с помощью JPA, Hibernate и Spring Data JPA
2. Спящий режим и JPA DAOS
Большинство производственных кодовых баз имеют какой-то уровень DAO. Обычно реализация варьируется от нескольких классов без абстрактного базового класса до какого-то обобщенного класса. Однако одно непротиворечиво – всегда существует более одного . Скорее всего, существует взаимно однозначная связь между ДАОСАМИ и сущностями в системе.
Кроме того, в зависимости от уровня используемых обобщений фактические реализации могут варьироваться от сильно дублированного кода до почти пустого, причем основная часть логики сгруппирована в базовый абстрактный класс.
Эти множественные реализации обычно могут быть заменены одним параметризованным DAO. Мы можем реализовать это таким образом, чтобы никакая функциональность не была потеряна, в полной мере воспользовавшись безопасностью типов, обеспечиваемой универсалиями Java.
Далее мы покажем две реализации этой концепции, одну для слоя персистентности, ориентированного на гибернацию, а другую-для JPA. Эти реализации ни в коем случае не являются полными, но мы можем легко добавить дополнительные методы доступа к данным.
2.1. Абстрактное понятие
Давайте быстро взглянем на класс AbstractHibernateDao :
public abstract class AbstractHibernateDao{ private Class clazz; @Autowired SessionFactory sessionFactory; public void setClazz(Class< T > clazzToSet){ this.clazz = clazzToSet; } public T findOne(long id){ return (T) getCurrentSession().get(clazz, id); } public List findAll() { return getCurrentSession().createQuery("from " + clazz.getName()).list(); } public T create(T entity) { getCurrentSession().saveOrUpdate(entity); return entity; } public T update(T entity) { return (T) getCurrentSession().merge(entity); } public void delete(T entity) { getCurrentSession().delete(entity); } public void deleteById(long entityId) { T entity = findOne(entityId); delete(entity); } protected Session getCurrentSession() { return sessionFactory.getCurrentSession(); } }
Это абстрактный класс с несколькими методами доступа к данным, который использует SessionFactory для манипулирования сущностями.
2.2. Универсальное ДАО Гибернации
Теперь, когда у нас есть абстрактный класс DAO, мы можем расширить его только один раз. Универсальная реализация DAO станет единственной реализацией нам нужна:
@Repository @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class GenericHibernateDaoextends AbstractHibernateDao implements IGenericDao { // }
Во-первых, обратите внимание, что общая реализация сама по себе параметризована , что позволяет клиенту выбирать правильный параметр в каждом конкретном случае. Это будет означать, что клиенты получат все преимущества безопасности типов без необходимости создавать несколько артефактов для каждой сущности.
Во-вторых, обратите внимание область прототипа этой общей реализации DAO . Использование этой области означает, что контейнер Spring будет создавать новый экземпляр DAO каждый раз, когда он запрашивается (в том числе при автопроводке). Это позволит службе использовать несколько DAO с различными параметрами для разных сущностей по мере необходимости.
Причина, по которой эта область так важна, связана с тем, как Spring инициализирует бобы в контейнере. Если оставить GenericDAO без области, это будет означать использование одноэлементной области по умолчанию, что приведет к тому, что один экземпляр DAO будет жить в контейнере . Это, очевидно, было бы в значительной степени ограничительным для любого более сложного сценария.
Genericdao – это просто интерфейс для всех методов DAO, чтобы мы могли внедрить необходимую нам реализацию:
public interface IGenericDao{ T findOne(final long id); List findAll(); void create(final T entity); T update(final T entity); void delete(final T entity); void deleteById(final long entityId); }
2.3. Абстрактный JPA DAO
AbstractJpaDao очень похож на AbstractHibernateDao:
public abstract class AbstractJpaDao< T extends Serializable > { private Class< T > clazz; @PersistenceContext EntityManager entityManager; public void setClazz( Class< T > clazzToSet ) { this.clazz = clazzToSet; } public T findOne( Long id ){ return entityManager.find( clazz, id ); } public List< T > findAll(){ return entityManager.createQuery( "from " + clazz.getName() ) .getResultList(); } public void save( T entity ){ entityManager.persist( entity ); } public void update( T entity ){ entityManager.merge( entity ); } public void delete( T entity ){ entityManager.remove( entity ); } public void deleteById( Long entityId ){ T entity = getById( entityId ); delete( entity ); } }
Аналогично реализации Hibernate DAO, мы используем API сохранения Java напрямую, не полагаясь на устаревшую Spring JpaTemplate .
2.4. Общий JPA DAO
Подобно реализации Hibernate, объект доступа к данным JPA также прост в использовании:
@Repository @Scope( BeanDefinition.SCOPE_PROTOTYPE ) public class GenericJpaDao< T extends Serializable > extends AbstractJpaDao< T > implements IGenericDao< T >{ // }
3. Введение Этого ДАО
Теперь у нас есть единый интерфейс DAO, который мы можем внедрить. Нам также необходимо указать класс :
@Service class FooService implements IFooService{ IGenericDaodao; @Autowired public void setDao(IGenericDao daoToSet) { dao = daoToSet; dao.setClazz(Foo.class); } // ... }
Spring автоматически подключил новый экземпляр DAO с помощью инъекции сеттера , чтобы можно было настроить реализацию с помощью объекта Class . После этого DAO полностью параметризован и готов к использованию службой.
Конечно, есть и другие способы указать класс для DAO – через отражение или даже в XML. Я предпочитаю это более простое решение из-за улучшенной читаемости и прозрачности по сравнению с использованием отражения.
4. Заключение
В этой статье обсуждалось упрощение уровня доступа к данным путем предоставления единой многоразовой реализации общего DAO. Мы показали реализацию как в спящем режиме, так и в среде, основанной на JPA. В результате получается упорядоченный слой персистентности, без ненужного беспорядка.
Пошаговое введение в настройку контекста Spring с использованием конфигурации на основе Java и базового pom Maven для проекта см. В этой статье .
Наконец, код для этой статьи можно найти в проекте GitHub .