62 votes

Couches DAO et services (JPA/Hibernate + Spring)

Je suis en train de concevoir une nouvelle application basée sur JPA/Hibernate, Spring et Wicket. La distinction entre les couches DAO et Service n'est pas très claire pour moi. Selon Wikipedia, DAO est

un objet qui fournit un à un certain type de base de données ou de base de données ou de mécanisme de persistance, fournissant certaines opérations spécifiques sans exposer les détails de la base de données.

Je me demandais si un DAO pouvait contenir des méthodes qui n'ont pas grand chose à voir avec l'accès aux données, mais qui sont beaucoup plus faciles à exécuter en utilisant une requête ? Par exemple, "obtenir une liste de toutes les compagnies aériennes qui opèrent sur un certain nombre d'aéroports" ? Il me semble que c'est plutôt une méthode de la couche service, mais je ne suis pas sûr que l'utilisation de JPA EntityManager dans la couche service soit un exemple de bonne pratique ?

52voto

walnutmon Points 1699

Une DAO doit donner accès à un seul connexe et, en fonction de la complexité de votre modèle d'entreprise, elle renverra soit des objets d'entreprise à part entière, soit de simples objets de données. Dans tous les cas, les méthodes DAO doivent refléter la base de données de manière assez proche.

Un service peut fournir une interface de niveau supérieur non seulement pour traiter vos objets commerciaux, mais aussi pour y accéder. Si j'obtiens un objet métier d'un service, cet objet peut être créé à partir de différentes bases de données (et de différents DAO), il peut être décoré avec des informations provenant d'une requête HTTP. Il peut être doté d'une certaine logique commerciale qui convertit plusieurs objets de données en un objet commercial unique et robuste.

Je crée généralement un DAO en pensant qu'il sera utilisé par tous ceux qui vont utiliser cette base de données, ou un ensemble de données liées à l'entreprise. Il s'agit littéralement du code de plus bas niveau, en dehors des déclencheurs, des fonctions et des procédures stockées dans la base de données.

Réponses à des questions spécifiques :

Je me demandais si un DAO pouvait contenir des méthodes qui n'ont pas vraiment l'accès aux données, mais qui sont mais qui sont beaucoup plus faciles à exécuter à l'aide d'une requête ?

Dans la plupart des cas, non, vous voudriez que votre logique d'entreprise la plus compliquée soit dans votre couche de service, l'assemblage des données à partir de requêtes séparées. Cependant, si la vitesse de traitement vous préoccupe, une couche de service peut déléguer une action à un DAO même si cela brise la beauté du modèle, de la même manière qu'un programmeur C++ peut écrire du code assembleur pour accélérer certaines actions.

Il me semble qu'il s'agit plutôt d'un méthode de la couche de service, mais je ne suis pas certain si utiliser JPA EntityManager dans la couche de service couche service est un exemple de bonne pratique ?

Si vous comptez utiliser votre gestionnaire d'entités dans votre service, considérez-le comme votre DAO, car c'est exactement ce qu'il est. Si vous devez supprimer une construction de requête redondante, ne le faites pas dans votre classe de service, extrayez-la dans une classe qui utilise le gestionnaire d'entités et faites-en votre DAO. Si votre cas d'utilisation est vraiment simple, vous pouvez sauter la couche de service entièrement et utiliser votre gestionnaire d'entité, ou DAO dans les contrôleurs parce que tout ce que votre service va faire est de passer des appels à getAirplaneById() de la DAO findAirplaneById()

MISE À JOUR - Pour clarifier la discussion ci-dessous, l'utilisation d'un gestionnaire d'entités dans un service n'est probablement pas la meilleure décision dans la plupart des situations où il y a également une couche DAO pour diverses raisons soulignées dans les commentaires. Mais à mon avis, ce serait parfaitement raisonnable étant donné :

  1. Le service doit interagir avec différents ensembles de données
  2. Au moins un ensemble de données a déjà un DAO.
  3. La classe de service réside dans un module qui nécessite une certaine persistance qui est suffisamment simple pour ne pas justifier son propre DAO.

exemple.

//some system that contains all our customers information
class PersonDao {
   findPersonBySSN( long ssn )
}

//some other system where we store pets
class PetDao {
   findPetsByAreaCode()
   findCatByFullName()
}

//some web portal your building has this service
class OurPortalPetLostAndFoundService {

   notifyOfLocalLostPets( Person p ) {
      Location l = ourPortalEntityManager.findSingle( PortalUser.class, p.getSSN() )
        .getOptions().getLocation();
      ... use other DAO's to get contact information and pets...
   }
}

14voto

Sean Patrick Floyd Points 109428

Une chose est certaine : si vous utilisez EntityManager sur la couche service, vous n'avez pas besoin d'une couche dao (une seule couche doit connaître les détails d'implémentation). En dehors de cela, il y a des opinions différentes :

  • Certains disent que l'EntityManager expose toutes les fonctionnalités nécessaires de l'annuaire, donc ils injectent EntityManager dans la couche de service.
  • D'autres ont une couche de dao traditionnelle soutenue par des interfaces (de sorte que la couche de service n'est pas liée aux détails de d'implémentation).

La seconde approche est plus élégante en termes de séparation des préoccupations et facilite le passage d'une technologie de persistance à l'autre (il suffit de réimplémenter les interfaces dao avec la nouvelle technologie), mais si vous savez que rien ne changera, la première approche est plus simple.

Je dirais que si vous avez un petit projet, utilisez JPA dans la couche service, mais dans un grand projet, utilisez une couche DAO dédiée.

5voto

Onur Points 442

Ce site article d'Adam Bien pourrait être utile.

4voto

Qwerky Points 10847

Traditionnellement, vous écrivez des interfaces qui définissent le contrat entre votre couche de services et votre couche de données. Vous écrivez ensuite des implémentations, qui constituent vos DAO.

Revenons à votre exemple. En supposant que la relation entre l'aéroport et la compagnie aérienne est de type "many to many" avec une table contenant airport_id et airline_id, vous pourriez avoir une interface ;

public interface AirportDAO
{
   public List<Airline> getAirlinesOperatingFrom(Set<Airport> airports);
}

et vous pourriez fournir une implémentation Hibernate de ceci ;

public class HibernateAirportDAO implements AirportDAO
{
   public List<Airline> getAirlinesOperatingFrom(Set<Airport> airports)
   {
      //implementation here using EntityManager.
   }
}

Vous pourriez également envisager d'avoir une liste sur votre entité Airline et de définir la relation avec une annotation JPA @ManyToMany. Cela supprimerait complètement la nécessité d'avoir cette méthode DAO particulière.

Vous pouvez également vous pencher sur le modèle Abstract Factory pour écrire des usines DAO. Par exemple ;

public abstract class DAOFactory
{
   private static HibernateDAOFactory hdf = new HibernateDAOFactory();

   public abstract AirportDAO getAirlineDAO();

   public static DAOFactory getFactory()
   {
      //return a concrete implementation here, which implementation you
      //return might depend on some application configuration settings.
   }
}

public class HibernateDAOFactory extends DAOFactory
{
   private static EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("myPersistenceUnit");

   public static EntityManager getEM()
   {
      return emFactory.createEntityManager();
   }

   public AirportDAO getAirportDAO()
   {
      return new HibernateAirportDAO();
   }
}

Ce modèle permet à votre HibernateDAOFactory de contenir un seul CEM et de fournir des CEM à des instances DAO individuelles. Si vous ne souhaitez pas emprunter la voie du CEM, Spring est capable de gérer les instances de DAO pour vous grâce à l'injection de dépendances.

Edit : Clarifié quelques hypothèses.

0voto

Mark Points 3549

Dao est un objet d'accès aux données. Il permet de stocker/mettre à jour/sélectionner des entités dans la base de données. L'objet gestionnaire d'entité est utilisé pour cela (au moins dans open jpa). Vous pouvez également exécuter des requêtes avec ce gestionnaire d'entités. Ce n'est pas du sql mais du JPQL (Java persistence query language).

Un exemple simple :

emf = Persistence.createEntityManagerFactory("localDB");
em = emf.createEntityManager();

Query q = em.createQuery("select u from Users as u where u.username = :username", Users.class);
q.setParameter("username", username);

List<Users> results = q.getResultList();

em.close();
emf.close();

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X