===== 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 ==== {{ epc_diagram_appli.png }} ** 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, , 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'' () 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 [[.:epcoreintro#generation_de_la_doc_du_modele|ici]]) === Les DAOs === Les tables suivantes ne nécessitent pas de classes DAOs: - prog_contact - proj_contact - study_contact - 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'' () ou ''launch configuration'' en spécifiant * Dans l'onglet **Main** * un nom, * **Console configuration** : le nom de la configuration hibernate créé (voir [[#preparation|ci-dessus]]), . * **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. {{ epc_hcg_dao.png }} ** 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. {{ epc_dao_diagram.png?800 }} ** 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 pour les cas simples et de pour les cas simples et de pour générée le code supplémentaire. :!: Utilisation des FQCN car pas d'ajout d'import | |Actor Définition des ''Role''s autorisées. :!: devrait être dans ActorRole ?! |Utilisation du tag 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 ''Type''s autorisées.. |Utilisation du tag 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 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 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 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 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 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 a 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'' () ou ''launch configuration'' en spécifiant * Dans l'onglet **Main** * un nom, * **Console configuration** : le nom de la configuration hibernate créé (voir [[#preparation|ci-dessus]]), . * **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// : PROPAGATION_REQUIRED,readOnly PROPAGATION_REQUIRED PROPAGATION_REQUIRED PROPAGATION_REQUIRED ... * Et pour les DAOs : \\ \\ \\ ---- **(Continuez vers la suite de la documentation => [[.:epcorespring]])**