Simplifiez le DAO avec Spring et Java Generics

Simplifier le DAO avec Spring et Java Generics

1. Vue d'ensemble

Cet article se concentrera sursimplifying the DAO layer en utilisant un seul objet d'accès aux données généré pour toutes les entités du système, ce qui se traduira par un accès aux données élégant, sans encombrement ni verbosité inutiles.

Nous allons construire sur la classe DAO abstraite que nous avons vue dansour previous article sur Spring et Hibernate, et ajouter la prise en charge des génériques.

Lectures complémentaires:

Introduction à Spring Data JPA

Introduction à Spring Data JPA avec Spring 4 - la configuration de Spring, le DAO, les requêtes manuelles et générées et la gestion des transactions.

Read more

Un guide de JPA avec le printemps

Configuration de JPA avec Spring - Comment configurer la fabrique EntityManager et utiliser les API JPA brutes.

Read more

Audit avec JPA, Hibernate et Spring Data JPA

Cet article présente trois approches pour introduire l'audit dans une application: JPA, Hibernate Envers et Spring Data JPA.

Read more

[more-722] #

2. Les DAO Hibernate et JPA

La plupart des bases de code de production ont une sorte de couche DAO. En général, l’implémentation varie de plusieurs classes sans classe de base abstraite à une sorte de classe générée. Cependant, une chose est cohérente - il y aalways more than one. Très probablement, il existe une relation un à un entre les DAO et les entités du système.

En outre, en fonction du niveau des génériques impliqués, les implémentations réelles peuvent varier d'un code fortement dupliqué à un code presque vide, la majeure partie de la logique étant regroupée dans une classe abstraite de base.

These multiple implementations can usually be replaced by a single parametrized DAO. Nous pouvons implémenter cela de telle sorte qu'aucune fonctionnalité ne soit perdue en tirant pleinement parti de la sécurité de type fournie par Java Generics.

Nous montrerons ensuite deux implémentations de ce concept, l'une pour une couche de persistance centrée Hibernate et l'autre axée sur JPA. Ces implémentations ne sont en aucun cas complètes, mais nous pouvons facilement ajouter d'autres méthodes d'accès aux données supplémentaires.

2.1. L'abstrait Hibernate DAO

Jetons un coup d'œil à la classeAbstractHibernateDao:

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();
    }
}

Il s'agit d'une classe abstraite avec plusieurs méthodes d'accès aux données, qui utilise lesSessionFactory pour manipuler les entités.

2.2. Le DAO Hibernate générique

Maintenant que nous avons la classe DAO abstraite, nous pouvons l'étendre une seule fois. The generic DAO implementation will become the only implementation dont nous avons besoin:

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

Tout d'abord,note that the generic implementation is itself parameterized, permettant au client de choisir le bon paramètre au cas par cas. Cela signifie que les clients bénéficient de tous les avantages de la sécurité de type sans avoir à créer plusieurs artefacts pour chaque entité.

Deuxièmement, notezthe prototype scope of this generic DAO implementation. L'utilisation de cette étendue signifie que le conteneur Spring créera une nouvelle instance du DAO à chaque fois qu'il sera demandé (y compris lors du câblage automatique). Cela permettra à un service d'utiliser plusieurs DAO avec différents paramètres pour différentes entités, selon les besoins.

La raison pour laquelle cette portée est si importante est due à la façon dont Spring initialise les haricots dans le conteneur. Laisser le DAO générique sans portée signifierait utiliserthe default singleton scope, which would lead to a single instance of the DAO living in the container. Cela serait évidemment très restrictif pour tout type de scénario plus complexe.

LeIGenericDao est simplement une interface pour toutes les méthodes DAO afin que nous puissions injecter l'implémentation dont nous avons besoin:

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. Le résumé JPA DAO

LeAbstractJpaDao est très similaire auAbstractHibernateDao:

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 );
   }
}

Semblable à l'implémentation Hibernate DAO, nous utilisons directement l'API Java Persistence, sans compter sur les SpringJpaTemplatedésormais obsolètes.

2.4. Le générique JPA DAO

Semblable à l'implémentation d'Hibernate, l'objet d'accès aux données JPA est également simple:

@Repository
@Scope( BeanDefinition.SCOPE_PROTOTYPE )
public class GenericJpaDao< T extends Serializable >
 extends AbstractJpaDao< T > implements IGenericDao< T >{
   //
}

3. Injecter ce DAO

We now have a single DAO interface we can inject. Nous devons également spécifier lesClass:

@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 afin que l'implémentation puisse être personnalisée avec l'objetClass. Après ce point, le DAO est entièrement paramétré et prêt à être utilisé par le service.

Il y a bien sûr d'autres moyens de spécifier la classe pour le DAO - par réflexion, voire en XML. Ma préférence va à cette solution plus simple en raison de l'amélioration de la lisibilité et de la transparence par rapport à l'utilisation de la réflexion.

4. Conclusion

Cet article a décrit lessimplification of the Data Access Layer en fournissant une implémentation unique et réutilisable d'un DAO générique. Nous avons montré l’implémentation dans un environnement basé sur Hibernate et JPA. Le résultat est une couche de persistance rationalisée, sans encombrement inutile.

Pour une introduction étape par étape sur la configuration du contexte Spring à l'aide de la configuration basée sur Java et du pom Maven de base pour le projet, consultezthis article.

Enfin, le code de cet article se trouve dansthe GitHub project.