web-dev-qa-db-fra.com

Couches DAO et Service (JPA/Hibernate + Spring)

Je conçois une nouvelle application basée sur JPA/Hibernate, Spring et Wicket. La distinction entre les couches DAO et Service n'est toutefois pas claire pour moi. Selon Wikipedia, DAO est 

un objet qui fournit un résumé interface à un type de base de données ou mécanisme de persistance, fournissant quelques opérations spécifiques sans exposer détails de la base de données.

Je me demandais si un DAO pouvait contenir des méthodes qui n’ont pas vraiment beaucoup à faire avec l’accès aux données, mais sont plus faciles à exécuter en utilisant une requête? Par exemple, "obtenir une liste de toutes les compagnies aériennes opérant sur un certain ensemble d'aéroports"? Cela me semble être davantage une méthode de couche de service, mais je ne suis pas sûr que l’utilisation de JPA EntityManager dans la couche de service constitue un exemple de bonne pratique.

62
John Manak

Un DAO doit donner accès à une seule source de données liée et, en fonction de la complexité de votre modèle commercial, renvoyer des objets métier à part entière ou de simples objets de données. Dans les deux cas, les méthodes DAO doivent refléter la base de données de façon assez précise.

Un service peut fournir une interface de niveau supérieur non seulement pour traiter vos objets métier, mais également 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 issues d'une requête HTTP. Il peut y avoir une certaine logique métier qui convertit plusieurs objets de données en un seul objet métier robuste.

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

Réponses à des questions spécifiques:

Je me demandais si un DAO pouvait contient des méthodes qui n'ont pas vraiment faire beaucoup avec l'accès aux données, mais sont manière plus facile exécuté à l'aide d'une requête?

dans la plupart des cas, non, vous voudriez que votre logique métier plus complexe dans votre couche service, soit l'assemblage de données provenant de requêtes distinctes. Toutefois, 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 être plus d'un méthode de couche de service, mais je ne suis pas sûr si vous utilisez JPA EntityManager dans le fichier couche de service est un exemple de bon entraine toi?

Si vous souhaitez utiliser votre responsable d'entité dans votre service, considérez-le comme votre DAO, car c'est exactement ce que c'est. Si vous devez supprimer un certain nombre de requêtes redondantes, ne le faites pas dans votre classe de service, extrayez-le dans une classe utilisant le gestionnaire d'entités et définissez-le comme votre DAO. Si votre cas d'utilisation est vraiment simple, vous pouvez ignorer entièrement la couche de service et utiliser votre responsable d'entité ou votre DAO dans les contrôleurs, car votre service ne fera que passer des appels de getAirplaneById() à findAirplaneById() du DAO.

MISE À JOUR - Pour clarifier la discussion ci-dessous, l'utilisation d'un responsable d'entité dans un service n'est probablement pas la meilleure décision dans la plupart des situations où il existe également une couche DAO pour diverses raisons mises en évidence 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 jeu de données possède déjà un DAO
  3. La classe de service réside dans un module qui nécessite une certaine persistance qui est assez simple pour ne pas garantir 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...
   }
}
51
walnutmon

Une chose est sûre: si vous utilisez EntityManager sur la couche de service, vous n’avez pas besoin d’une couche Dao (une seule couche devrait connaître les détails de la mise en oeuvre). En dehors de cela, il y a des opinions différentes:

  • Certains disent que EntityManager expose Toutes les fonctionnalités nécessaires au dao, alors, Injecte EntityManager dans la couche service
  • D'autres ont une couche .o. Traditionnelle de Dao soutenue par des interfaces (ainsi, la couche de service N'est pas liée aux détails d'implémentation ).

La deuxième approche est plus élégante en ce qui concerne la séparation des problèmes et facilite également le passage d’une technologie de persistance à l’autre (vous devez simplement réimplémenter les interfaces de Dao avec la nouvelle technologie), mais si vous savez que rien ne change. va changer, le premier est plus facile.

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

14

Ce article d'Adam Bien pourrait être utile. 

5
Onur

En règle générale, vous écrivez des interfaces qui définissent le contrat entre votre couche de service et votre couche de données. Vous écrivez ensuite des implémentations et ce sont vos DAO.

Retour à votre exemple. En supposant que la relation entre l'aéroport et la compagnie aérienne soit très diverse, 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 pouvez fournir une implémentation Hibernate de ceci;

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

Vous pouvez également envisager de créer une liste sur votre entité Compagnie aérienne et de définir la relation avec une annotation JPA @ManyToMany. Cela éliminerait la nécessité de disposer totalement de cette méthode DAO.

Vous voudrez peut-être aussi examiner 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 EMF et de fournir des instances DAO individuelles avec des ME. Si vous ne voulez pas emprunter le chemin du fatory, alors Spring est très doué pour gérer les instances DAO avec une injection de dépendance.

Edit: Clarifié quelques hypothèses.

4
Qwerky

Dao est un objet d'accès aux données. Il stocke/met à jour/sélectionne des entités dans la base de données. L'objet gestionnaire d'entités est utilisé pour cela (au moins en open jpa). Vous pouvez également exécuter des requêtes avec ce gestionnaire d'entités. Ce n'est pas SQL mais JPQL (langage de requête de persistance Java).

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();
0
Mark Baijens