User Tools

Site Tools


wiki:epims4_1m1:developer:epcorearchitecture

Architecture et génération des DAOs/POJOs

Cette section décrit l’organisation des packages ainsi que les points d’interaction entre les objets des différents frameworks. eP-Core ne concernant pas tout ce qui se rapporte à la partie présentation, seuls les objets de persistance de données et ceux propre à la logique applicative sont définis.

La théorie

Figure: Diagramme de classe globale

  • Les objets du package BO (BusinessObject) représentent les données du model (en l’occurrence celles de la base de données). Ces objets ne contiennent que des informations (des attributs) mais pas de ‘comportements’. Ils sont accessibles dans toutes les couches de l’application.
  • Le package DAO contient les classes qui ont la charge de la persistance des données (utilisation du framework Hibernate). Seules les interfaces doivent être accédées afin de permettre tout changement d’implémentation de manière transparente. En effet, le framework Spring permet de spécifier (aux classes services) la classe d’implémentation via le mécanisme d’inversion de contrôle (IoC) par injection par accesseur : utilisation d’un fichier de configuration et accès aux setter méthodes. Spring prend également en charge la gestion des sessions/transactions pour Hibernate : les classes DAO doivent étendre la classe HibernateDaoSupport qui prend en charge les accès JDBC.
  • Le package Service contient les interfaces qui doivent être utilisées par toutes les autres applications (ou couches) afin de manipuler (créer, modifier, supprimer) les objets de données. Les classes d’implémentation des services doivent définir des setter méthodes pour les classes DAO afin d’être configurées par Spring (voir paragraphe précédent).

En résumé, la couche model rend visible d’une part les objets représentant les données (package BO) et, d’autre part, les interfaces Services qui permettent de manipuler ces données.

On retrouve donc dans les fichiers de configuration xml : (spring-*.xml)

  • La définition de la dataSource utilisée par Hibernate pour accéder à la DB.
  • La définition des différentes propriétés propres à Hibernate (mapping files…)
  • La définition des dao : description des beans et spécification de la propriété hibernateTemplate.
  • La définition des services : description du bean et spécification des propriétés qui seront positionnées via les accesseurs.

REMARQUE : ServiceLocator recherche dans l’ApplicationContext qui lui est spécifié les beans correspondants à l’implémentation de IXXXService (définit dans spring-*.xml).

La pratique

L'objectif est d'obtenir la même architecture que celle décrite ci-dessus mais en utilisant les outils de génération automatique et en évitant au maximum de devoir définir des “template” maison.

Le fichier ./hibernate.reveng.xml est le fichier de Reverse Engineering de Hibernate. Il permet de spécifier, entre autre, des noms de classes différentes que ceux proposés par défaut pour certaines tables.

  o table ms_protocol  => classe cea.edyp.epims.domain.dao.impl.MSProtocol
  o table msms_protocol => classe cea.edyp.epims.domain.dao.impl.MSMSProtocol
  o table gel_1d => classe cea.edyp.epims.domain.dao.impl.Gel1D
  o table gel_2d => classe cea.edyp.epims.domain.dao.impl.Gel2D
  o table gel_1d_migration  => classe cea.edyp.epims.domain.dao.impl.Gel1DMigration
  o table gel_2d_migration  => classe cea.edyp.epims.domain.dao.impl.Gel2DMigration
  o table sample_species => classe cea.edyp.epims.domain.dao.impl.Species
  o table sample_subcellular_localisation => classe cea.edyp.epims.domain.dao.impl.SubCellularLocalisation




Remarque

L'architecture du projet eP-Core suite à l'utilisation de Hibernate Tools n'est pas celle de la version distribuée (eP-Core 3.4.6) dans ePims 4.0M2 mais elle est intégrée à partir de la version 3.5.0 (seule cette intégration différencie les versions)

Actuellement pour la génération automatique nous utilisons Hibernate Tools comme plugin Eclipse. Les règles / spécificités que l'on a souhaité conserver :

  • Absolument
    • séparation des POJOs et DAOs dans des packages différents : OK Avec Hibernate Tools !
    • séparation interface / implémentation pour les DAOs : OK Avec Hibernate Tools !

La séparation en deux niveaux de hiérarchie pour les POJOs n'est pas possible (a moindre coût … après une rapide évaluation).

De plus les modifications entrainées par le changement de générateur doivent impacter au minimum les modules utilisant eP-Core.



Préparation

Pour les différentes générations automatiques décrites ici, il est nécessaire de définir une configuration hibernate.
Remarque le fichier \eP-Core\resources\hibernate\hibernate.cfg.xml utilisé par Hibernate Tools ne défini pas de connexion à la BD puisque les fichiers hbm.xml sont créés manuellement et les objets générés le sont à partir de ces fichiers hbm. Il n'y a donc pas de connexion à la BD pour la génération automatique.


1.
Dans la perspective Hibernate et la vue Hibernate Configurations, cliquer droit et choisir “add Configuration”. Choisir un nom, <ePCore_config>, et saisir les informations suivantes:

  • Dans l'onglet Main :
    • type : Core
    • Project : eP-Core
    • Configuration file : \eP-Core\resources\hibernate\hibernate.cfg.xml
  • Dans l'onglet Mappings :
    • Entrer tous les fichiers hbm.xml qui sont définis dans /eP-Core/src/mapping/cea/edyp/epims/domain/mapping
  • Cliquer sur “Ok” pour créer la configuration


2.
De la même manière, il faut définir un Hibernate Code Generation (<HCG>) ou launch configuration (Bouton “Run Hibernate”, Option “Open Hibernate Configuration Dialog”). Un même HCG peut être utiliser pour générer en une seule fois tous les types d'objets et documents (POJOs, DAO, modèle). Mais ceci peut poser un problème puisque tous les objets ne doivent pas être générés à chaque fois !! A chacun de gérer sa/ses HCG et à être attentif aux objets générés lors de l'exécution de celui-ci ! Nous décrirons dans la doc trois configurations différentes selon que l'on souhaite générer :

  • les DAO (voir chapitre ci-dessous)
  • les objets du domaines (voir chapitre “Les POJOs”)
  • la documentation du modèle (voir ici)

Les DAOs

Les tables suivantes ne nécessitent pas de classes DAOs:

  1. prog_contact
  2. proj_contact
  3. study_contact
  4. samples_share
Génération

Les DAO sont générés dans le répertoire source “src/main” et

  • le package “cea.edyp.epims.domain.dao.impl” pour les classes d'implémentation. Une méthode static permet d'obtenir une instance de la classe à partir d'un context spring. Cette méthode suppose que le bean correspondant à cette classe est identifié par un nom = nom de la classe !
  • le package “cea.edyp.epims.domain.dao” pour les interfaces. Seules les interfaces sont référencées dans les services.

Pour cela, depuis Eclipse, ouvrir la perspective Hibernate et accéder à la boite de dialogue Hibernate Code Generation, (en passant par le menu Run > Hibernate Code Generation > Open Hibernate Code Generation Dialog).

Créer un Hibernate Code Generation (<HCG>) ou launch configuration en spécifiant

  • Dans l'onglet Main
    • un nom,
    • Console configuration : le nom de la configuration hibernate créé (voir ci-dessus), <ePCore_config>.
    • Output directory : \eP-Core\src\main
  • Dans l'onglet Exporters
    • Sélectionner l'option “Use Java 5 Syntax” (afin d'utiliser les Generic)
    • Selon que l'on souhaite générer les interfaces et/ou les classes pour les DAO, il faut sélectionner un ou deux Generic Exporter configuré(s) comme ceux indiqués ci-dessous. On peut ajouter et configurer autant de Generic Exporter que voulu en sélectionnant add dans l'onglet Exporters.
      • Les paramètres pour les templates et le pattern pour la localisation et le nom de fichier diffèrent.
      • Remarque : Il est possible d'exécuter le(s) exporter(s) que sur un sous-ensemble de fichiers mapping (les nouveaux par exemple). ceci afin de ne pas écraser les interfaces existantes… Pour cela il faut utiliser une console configuration dans laquelle seuls ces fichiers auront été spécifiés.

Figure: Génération automatique des DAO Hibernate

Attention: Certaines interfaces sont enrichies. La génération automatique ne doit donc pas être faite systématiquement et doit être faite avec précaution !

Après génération des classes DAO, certaines modifications manuelles sont nécessaires. En effet, ces classes sont générées en fonction de templates correspondant à la définition des interfaces. Si des modifications manuelles sont faites au niveau des interfaces, il est nécessaire de les répercuter au niveau des classes.
Par exemple (seul exemple pour le moment ! ), pour l'objet Study :

  • Les objets du domaines cea.edyp.epims.domain.AbstractStudy et cea.edyp.epims.domain.Study sont généres (cf ci-dessous)
  • La classe cea.edyp.epims.domain.dao.impl.StudyDAO et l'interface cea.edyp.epims.domain.dao.IStudyDAO ont été générées.
  • La méthode refreshLazy() a été ajoutée à l'interface IStudyDAO. Nous avons défini une classe EPCoreStudyDAO dérivée de StudyDAO qui implémente cette méthode. Par contre, la classe StudyDAO n'implémente pas toutes les méthodes de IStudy, par conséquent une erreur est levée. Pour remédier à cela, la classe StudyDAO est déclarée abstraite.

Figure: Diagramme de classes exemple pour les DAO Hibernate

Les POJOs

Les Objets du domaines sont générés depuis les fichiers *.hbm.xml, dans le répertoire source src/main.

Génération

Attention:
La génération est en cour de mise au point !!

Hibernate Tools ne permet pas de générer pour chaque classe du domaine, une classe abstraite. Pour le moment, il a été choisi de :

  • Revoir la hiérarchie des classes du domaines pour adopter celle générée par Hibernate Tools. Pour le code propriétaire (non généré) il est possible de le faire apparaitre au niveau des hbm.xml (cf ci-dessous)


Dans le cas où cette solution n'est plus satisfaisante, une autre solution serait de créer un ReverseEngineeringStrategy propriétaire pour générer les 2 niveaux qui existaient.

1. Le “Custom code”

Intégration du Custom code définis dans les versions précédentes avec HibernateTools:

Custom Code Intégration dans HibernateTools
Méthode equals Utilisation du tag <meta attribute=“use-in-equals”> pour les cas simples et de <meta attribute=“class-code” pour une définition plus complète
Méthode hashcode Possibilité d'utiliser <meta attribute=“class-code” si nécessaire
Méthode toString Possibilité d'utiliser <meta attribute=“use-in-tostring”> pour les cas simples et de <meta attribute=“class-code” pour une définition plus complète
AcquisitionResult . Méthode Boolean getArchived() retourne not null object Impossible de redéfinir cette méthode. La colonne ayant une valeur par défaut (false), utilisation de la méthode générée. A l'utilisateur de faire les vérifications ! Peut aussi défini getNoneNullArchived() ?!
Acquisition Définition des Natures autorisées. Utilisation du tag <meta attribute=“class-code”> pour générée le code supplémentaire. :!: Utilisation des FQCN car pas d'ajout d'import
Actor Définition des Roles autorisées. :!: devrait être dans ActorRole ?! Utilisation du tag <meta attribute=“class-code”> pour générée le code supplémentaire. :!: Déplacé effectivement dans ActorRole (qui n'existait pas au début). Utilisation des FQCN car pas d'ajout d'import
AttachedFile Définition des Types autorisées.. Utilisation du tag <meta attribute=“class-code”> pour générée le code supplémentaire. :!: Utilisation des FQCN car pas d'ajout d'import
Program : Méthodes isClosed et CloseProgram Utilisation du tag <meta attribute=“class-code”> pour générée le code supplémentaire. :!: Utilisation des FQCN car pas d'ajout d'import
Project : Méthodes isClosed et CloseProject Utilisation du tag <meta attribute=“class-code”> pour générée le code supplémentaire. :!: Utilisation des FQCN car pas d'ajout d'import
Sample Définition des Status autorisées.. Utilisation du tag <meta attribute=“class-code”> pour générée le code supplémentaire. :!: Utilisation des FQCN car pas d'ajout d'import
Study Définition des Status autorisées.. Utilisation du tag <meta attribute=“class-code”> pour générée le code supplémentaire. :!: Utilisation des FQCN car pas d'ajout d'import
VirtualPlate Méthodes addWell et isFull Utilisation du tag <meta attribute=“class-code”> pour générée le code supplémentaire. :!: Utilisation des FQCN car pas d'ajout d'import
VirtualWell surcharge des constructeurs ayant un VirtualPlate comme paramètre pour appeler VirtualPlate.addWell Impossible de surcharger. :!: Déplace la fonctionnalité dans le service IRobotPlanningService

2. Initialisation de l'ID
Jusque là, l'appel suivant était ajouté dans les constructeurs pour initialisation de l'ID à la valeur spécifiée comme unsaved-value dans le hbm.xml

super.setId(-1);     //Set id to -1 => unsaved-value

Pour les classes:

  • Acquisition
  • AcquisitionProtocol
  • AcquisitionResult
  • Aliquotage
  • AttachedFile
  • BiologicOrigin
  • ChromatographyColumn
  • Contact
  • Gel1D
  • Instrument
  • MassPlanning
  • Mix
  • Program
  • Project
  • Protocol
  • ProtocolStep
  • RobotPlanning
  • RunRobot
  • SampleAttachedFile
  • SampleKind
  • SampleLocator
  • SampleType
  • SeparationResult
  • Species
  • Study
  • StudyAttachedFile
  • SubCellularLocalisation
  • Support
  • VirtualWell

A partir de la version 3.5.0 d'eP-Core, la définition de l'id dans les hbm.xml a été changé de

<id ... type="int" unsaved-value="-1"

a

<id ... type="Integer" unsaved-value="null"

Par conséquent, il n'y a pas de code spécifique à ajouter dans les constructeurs.

3. Gestion des composite-id
Voir comment sont gérés les composite-id dans hibernate tools: ActorRoleId, TreatmentsApplicationId

⇒ Hibernate Tools ne défini pas de classes spécifique pour les composite ID… seulement tous les paramètres appartenant à la clé diovent être spécifiés !

4. Génération avec Hibernate Tools

Les POJOs sont générés dans le répertoire source “src/main” et le package cea.edyp.epims.domain Pour cela, depuis Eclipse, ouvrir la perspective Hibernate et accéder à la boite de dialogue Hibernate Code Generation, (en passant par le menu Run > Hibernate Code Generation > Open Hibernate Code Generation Dialog).

Créer un Hibernate Code Generation (<HCG>) ou launch configuration en spécifiant

  • Dans l'onglet Main
    • un nom,
    • Console configuration : le nom de la configuration hibernate créé (voir ci-dessus), <ePCore_config>.
    • Output directory : \eP-Core\src\main
  • Dans l'onglet Exporters
    • Sélectionner l'option “Use Java 5 Syntax” (afin d'utiliser les Generic)
    • Sélectionner Domain Code (Java) et le configurer avec la propriété Template directory = \eP-Core\resources\hibernate\templates

Exécuter la génération en sélectionnant Run …

Les Services

Accès

Seules les interfaces IxxxService sont visibles depuis toute autre application (telle que la couche présentation) qui utilisera eP-Core. Ces interfaces devront fournir les méthodes nécessaires pour l’accès aux données en lecture et en écriture.

Règles de développement

Les méthodes pour l'écriture des données sont de la forme :

  • public Sample saveSample(Sample sample)throws EPimsServiceException; Sauvegarde d'un élément (ici Sample) déjà existant. L'objet sauvegardé est retourné.
  • public Sample createSample(Sample sample) throws EPimsServiceException; Création d'un nouvel élément. L'objet créé est retourné.
  • public void deleteSample(Sample sample) throws EPimsServiceException; Suppression d'un élément

Pour les accès en lecture, les services sont écrits selon le principe suivant :

L'accès en écriture est pour l'essentiel géré par des requêtes. Ces requêtes retournent des objets du domaines suivant des filtres / caractéristiques bien définis. Par conséquent, on appellera la requête X qui retourne les études “blabla” par la méthode List getStudy(X) Dans l'interface Service (XXXService), on définit un ensemble d'enum. Chacun des enum étant des requêtes qui retourne un même type de donnée. Ainsi, on aura:

  • Les méthodes : List getObjA(ObjAQueries), List getObjB(ObjBQueries) et mêmes méthodes avec des paramètres pour les paramètres des queries.
  • enum ObjAQueries qui définit un ensemble de requetes pouvant être passé en paramètre à getObjA et retournant des objets de type ObjA
  • enum ObjBQueries qui définit un ensemble de requêtes pouvant être passé en paramètre à getObjB et retournant des objets de type ObjB

Pour les données annexes de type autre que des objets du domaine (nombre d'acquisitions, liste des modèle de spectro disponible, …) on définit :

  • une méthode List getAssociatedXXXObjects(AssociatedXXXQueries)
  • enum AssociatedXXXQueries qui définit un ensemble de requêtes pouvant être passé en paramètre à getAssociatedXXXObjects

Il est certain que certaines actions ne peuvent être appliquées par une simple requête (aussi compliquée qu'elle puisse être !!), à ce moment là, les services peuvent aussi avoir des méthodes exécutant des tâches partiulières.

Faire le lien avec Spring

les classes d'implémantation des services utilisent donc des objets typés par les interfaces des DAOs. Spring gère le lien entre ces objets :

* Dans le fichier SpringAppContext.xml :

     <bean id="contactServiceTarget" class="cea.edyp.epims.service.impl.HibernateContactService"> 
         <property name="actorDao"><ref bean="ActorDAO"></ref></property>
         <property name="contactDao"><ref bean="ContactDAO"></ref></property>
         <property name="actorRoleDao"><ref bean="ActorRoleDAO"></ref></property>
         <property name="companyDao"><ref bean="CompanyDAO"></ref></property>
      </bean>
        <!-- Transactional proxy Services -->   
      <bean id="baseTransactionProwy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"  abstract="true">
         <property name="transactionManager"><ref local="transactionManager"/></property>
         <property name="transactionAttributes">
             <props>
                 <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
                 <prop key="save*">PROPAGATION_REQUIRED</prop>
                 <prop key="create*">PROPAGATION_REQUIRED</prop>
                 <prop key="delete*">PROPAGATION_REQUIRED</prop>
             </props>
         </property>
     </bean>
     ...
     <bean id="contactService" parent="baseTransactionProxy">
        <property name="target"><ref local="contactServiceTarget"/></property>
    </bean>
  • Et pour les DAOs :
    <bean id="ActorDAO" class="cea.edyp.epims.domain.dao.impl.HibernateActorDAO"  >
        <property name="hibernateTemplate"><ref bean="hibernateTemplate"/></property>
    </bean>





(Continuez vers la suite de la documentation ⇒ epcorespring)

wiki/epims4_1m1/developer/epcorearchitecture.txt · Last modified: 2010/07/16 11:42 by 132.168.73.247