Рубрики
Без рубрики

Упростите DAO с помощью дженериков Spring и Java

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

Автор оригинала: 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 GenericHibernateDao
  extends 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{

   IGenericDao dao;

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