Упростите DAO с помощью Spring и Java Generics

Упростите DAO с помощью Spring и Java Generics

1. обзор

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

Мы построим на абстрактном классе DAO, который мы видели вour previous article в Spring и Hibernate, и добавим поддержку универсальных шаблонов.

Дальнейшее чтение:

Введение в Spring Data JPA

Введение в Spring Data JPA с Spring 4 - конфигурация Spring, DAO, ручные и сгенерированные запросы и управление транзакциями.

Read more

Руководство по JPA с весны

Настройка JPA с помощью Spring - как настроить фабрику EntityManager и использовать необработанные API JPA.

Read more

Аудит с использованием JPA, Hibernate и Spring Data JPA

В этой статье демонстрируются три подхода к внедрению аудита в приложение: JPA, Hibernate Envers и Spring Data JPA.

Read more

[more-722] #

2. DAO Hibernate и JPA

Большинство производственных кодовых баз имеют некоторый уровень DAO. Обычно реализация варьируется от нескольких классов без абстрактного базового класса до какого-то обобщенного класса. Однако непротиворечиво одно - естьalways more than one. Скорее всего, между DAO и объектами в системе существует отношение один к одному.

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

These multiple implementations can usually be replaced by a single parametrized DAO. Мы можем реализовать это так, чтобы никакая функциональность не была потеряна, используя все преимущества безопасности типов, обеспечиваемые Java Generics.

Далее мы покажем две реализации этой концепции: одну для уровня сохраняемости, ориентированного на Hibernate, а другую - для JPA. Эти реализации ни в коем случае не завершены, но мы можем легко добавить дополнительные методы доступа к данным.

2.1. Абстрактная Hibernate DAO

Давайте быстро взглянем на класс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. Универсальный Hibernate DAO

Теперь, когда у нас есть абстрактный класс DAO, мы можем расширить его только один раз. The generic DAO implementation will become the only implementation нам понадобится:

@Repository
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class GenericHibernateDao
  extends AbstractHibernateDao implements IGenericDao{
   //
}

Во-первых,note that the generic implementation is itself parameterized, позволяющий клиенту выбрать правильный параметр в каждом конкретном случае. Это будет означать, что клиенты получают все преимущества безопасности типов без необходимости создавать несколько артефактов для каждого объекта.

Во-вторых, обратите внимание наthe prototype scope of this generic DAO implementation. Использование этой области означает, что контейнер Spring будет создавать новый экземпляр DAO каждый раз, когда он запрашивается (в том числе при автоматическом подключении). Это позволит службе при необходимости использовать несколько DAO с разными параметрами для разных объектов.

Причина, по которой эта область так важна, заключается в том, что Spring инициализирует компоненты в контейнере. Если оставить общий DAO без области видимости, значит использоватьthe default singleton scope, which would lead to a single instance of the DAO living in the container. Это, очевидно, будет в значительной степени ограничивающим фактором для любого более сложного сценария.

IGenericDao - это просто интерфейс для всех методов 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, мы используем Java Persistence API напрямую, не полагаясь на устаревший SpringJpaTemplate.

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

We now have a single DAO interface we can inject. Нам также необходимо указатьClass:

@Service
class FooService implements IFooService{

   IGenericDao dao;

   @Autowired
   public void setDao(IGenericDao daoToSet) {
      dao = daoToSet;
      dao.setClazz(Foo.class);
   }

   // ...
}

Springautowires the new DAO instance using setter injection, чтобы реализацию можно было настроить с помощью объектаClass. После этого DAO полностью параметризован и готов к использованию службой.

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

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

В этой статье были рассмотреныsimplification of the Data Access Layer путем предоставления единой многоразовой реализации универсального DAO. Мы продемонстрировали реализацию как в среде Hibernate, так и в среде на основе JPA. Результатом является упорядоченный слой постоянства, без лишних помех.

Пошаговое введение в настройку контекста Spring с использованием конфигурации на основе Java и базового Maven pom для проекта см. Вthis article.

Наконец, код для этой статьи можно найти вthe GitHub project.