1. Обзор
В этой статье основное внимание будет уделено упрощению уровня DAO за счет использования единого обобщенного объекта доступа к данным для всех объектов в системе, что приведет к элегантному доступу к данным без лишних помех и многословия.
2. Hibernate и JPA DAOs
Большинство производственных кодовых баз имеют некоторый уровень DAO. Обычно реализация варьируется от нескольких классов без абстрактного базового класса до какого-то обобщенного класса. Однако одно непротиворечиво — всегда больше одного — скорее всего, существует отношение один к одному между DAO и объектами в системе.
Кроме того, в зависимости от уровня используемых обобщений, фактические реализации могут варьироваться от сильно дублированного кода до почти пустого, причем основная часть логики сгруппирована в базовом абстрактном классе.
Эти множественные реализации обычно могут быть заменены одним параметризованным DAO, используемым таким образом, что никакие функциональные возможности не будут потеряны, если в полной мере использовать безопасность типов, обеспечиваемую Java Generics.
Далее представлены две реализации этой концепции, одна для ориентированного на Hibernate уровня персистентности, а другая для JPA . Эти реализации ни в коем случае не завершены — включены только некоторые методы доступа к данным, но их можно легко сделать более тщательными.
2.1. Абстрактная Hibernate DAO
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | publicabstractclassAbstractHibernateDao< T extendsSerializable > {   privateClass< T > clazz;   @Autowired   SessionFactory sessionFactory;   publicfinalvoidsetClazz( Class< T > clazzToSet ){      this.clazz = clazzToSet;   }   publicT findOne( longid ){      return(T) getCurrentSession().get( clazz, id );   }   publicList< T > findAll(){      returngetCurrentSession().createQuery( "from "+ clazz.getName() ).list();   }   publicvoidcreate( T entity ){      getCurrentSession().persist( entity );   }   publicvoidupdate( T entity ){      getCurrentSession().merge( entity );   }   publicvoiddelete( T entity ){      getCurrentSession().delete( entity );   }   publicvoiddeleteById( longentityId ){      T entity = findOne( entityId );      delete( entity );   }   protectedfinalSession getCurrentSession(){      returnsessionFactory.getCurrentSession();   }} | 
DAO использует Hibernate API напрямую, без использования каких-либо шаблонов Spring (таких как HibernateTemplate ). Использование шаблонов, а также управление SessionFactory, которая автоматически подключается в DAO, были рассмотрены в руководстве Hibernate DAO .
2.2. Универсальный Hibernate DAO
Теперь, когда абстрактный DAO готов, мы можем реализовать его только один раз — общая реализация DAO станет единственной необходимой реализацией :
| 1 2 3 4 5 6 | @Repository@Scope( BeanDefinition.SCOPE_PROTOTYPE )publicclassGenericHibernateDao< T extendsSerializable >  extendsAbstractHibernateDao< T > implementsIGenericDao< T >{   //} | 
Во-первых, обратите внимание, что общая реализация сама параметризована, что позволяет клиенту выбирать правильный параметр в каждом конкретном случае. Это будет означать, что клиенты получают все преимущества безопасности типов без необходимости создавать несколько артефактов для каждого объекта.
Во-вторых, обратите внимание на прототип этой общей реализации DAO. Использование этой области означает, что контейнер Spring будет создавать новый экземпляр DAO каждый раз, когда он запрашивается (в том числе при автоматическом подключении). Это позволит службе при необходимости использовать несколько DAO с разными параметрами для разных объектов.
Причина, по которой эта область так важна, заключается в том, как Spring инициализирует bean-компоненты в контейнере. Оставление общего DAO без области действия будет означать использование одноэлементной области по умолчанию, что приведет к тому, что в контейнере будет жить один экземпляр DAO. Это, очевидно, будет в значительной степени ограничивающим фактором для любого более сложного сценария.
IGenericDao — это просто интерфейс для всех методов DAO, так что мы можем внедрить нашу реализацию со Spring в (или во что угодно):
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 | publicinterfaceIGenericDao<T extendsSerializable> {   T findOne(finallongid);   List<T> findAll();   voidcreate(finalT entity);   T update(finalT entity);   voiddelete(finalT entity);   voiddeleteById(finallongentityId);} | 
2,3. Абстрактная JPA DAO
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | publicabstractclassAbstractJpaDao< T extendsSerializable > {   privateClass< T > clazz;   @PersistenceContext   EntityManager entityManager;   publicvoidsetClazz( Class< T > clazzToSet ){      this.clazz = clazzToSet;   }   publicT findOne( Long id ){      returnentityManager.find( clazz, id );   }   publicList< T > findAll(){      returnentityManager.createQuery( "from "+ clazz.getName() )       .getResultList();   }   publicvoidsave( T entity ){      entityManager.persist( entity );   }   publicvoidupdate( T entity ){      entityManager.merge( entity );   }   publicvoiddelete( T entity ){      entityManager.remove( entity );   }   publicvoiddeleteById( Long entityId ){      T entity = getById( entityId );      delete( entity );   }} | 
Как и в реализации Hibernate DAO, API персистентности Java используется здесь напрямую, опять же, не полагаясь на устаревший Spring JpaTemplate .
2,4. Общий JPA DAO
Подобно реализации Hibernate, объект доступа к данным JPA также прост:
| 1 2 3 4 5 6 | @Repository@Scope( BeanDefinition.SCOPE_PROTOTYPE )publicclassGenericJpaDao< T extendsSerializable > extendsAbstractJpaDao< T > implementsIGenericDao< T >{   //} | 
3. Внедрение этого DAO
В настоящее время Spring вводит один DAO ; также класс должен быть указан:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 | @ServiceclassFooService implementsIFooService{   IGenericDao< Foo > dao;   @Autowired   publicvoidsetDao( IGenericDao< Foo > daoToSet ){      dao = daoToSet;      dao.setClazz( Foo.class);   }   // ...} | 
Spring автоматически подключает новый DAO, используя инжекцию сеттера, так что реализацию можно настроить с помощью объекта Class . После этого DAO полностью параметризован и готов к использованию службой.
Конечно, есть и другие способы, которыми класс может быть указан для DAO — с помощью отражения или даже в XML. Я предпочитаю это более простое решение из-за улучшенной читаемости и прозрачности по сравнению с использованием отражения.
4. Вывод
В этой статье обсуждается упрощение уровня доступа к данным путем предоставления единой многократно используемой реализации общего DAO. Эта реализация была представлена как в Hibernate, так и в среде на основе JPA. Результатом является упорядоченный слой постоянства, без лишних помех.
Пошаговое введение по настройке контекста Spring с использованием конфигурации на основе Java и базового Maven pom для проекта см. В этой статье .