560 likes | 750 Views
Chapitre 2 Persistance de données De Hibernate au standard JPA 1.0. Sommaire. Stratégies d'accès aux données Principes du mappage Objet/Relationnel Le Framework Hibernate 3 La JPA 1.0 (Java Persistance API) Synthèse comparative . Critères de choix de la stratégie de persistance.
E N D
Chapitre 2Persistance de donnéesDe Hibernate au standard JPA 1.0
Sommaire • Stratégies d'accès aux données • Principes du mappage Objet/Relationnel • Le Framework Hibernate 3 • La JPA 1.0 (Java Persistance API) • Synthèse comparative
Critères de choix de la stratégie de persistance • Les critères de choix sont : • La performance • La portabilité • L'indépendance vis à vis des bases de données • Prise en compte des relations • Facilités de développement • Technique de Caching • Le modèle de persistance (O/R mapping) • Migration
Stratégies de persistance • JDBC plat avec objets ADO • Framework de persistance du type Hibernate/JDO/Castor • EJB Entity • Avec persistance CMP • Avec persistance BMP • EJB Session avec JDBC
La persistance des objet • La persistance d’objets désigne le fait que des objets individuels peuvent survivre au processus de l’application. • Ils peuvent être enregistrés dans un entrepôt de données et récupérés par la suite. • Le mappage Objet/Relationnel (ORM) désigne la persistance automatisée et transparente d’objets dans une application Java vers les tables d’une base de données relationnelle à l’aide des méta données décrivant le mapping entre les objets et la base de données.
Gestion de la correspondance objet/relationnel • 30% du code java est consacré à gérer la mise en rapport SQL/JDBC et la non correspondance entre le modèle objet et le modèle relationnel • Gestion des associations • Correspondances de types • Granularité • Les outils et frameworks ORM réduisent grandement le code qui permet cette correspondance et facilitent donc les adaptations mutuelles des deux modèles.
ORM( Object Relationnel Mapping) • C’est une technique de mapping d’une représentation des données d’un modèle objet vers un modèle relationnel de base de données et inversement. • Quatre éléments constituant une solution ORM : • Une API pour effectuer les opérations CRUD (Create, Read, Update, delete) de base sur les objets des classes persistantes. • Un langage ou une API pour spécifier des requêtes qui se réfèrent aux classes et aux propriétés des classes. • Un système pour spécifier des métadonnées de mapping. • Une technique pour l’implémentation du mapping objet/relationnel permettant d’interagir avec des objets transactionnels pour effectuer des vérifications brutes, rechercher des associations lâches et implémenter d’autres fonctions d’optimisation.
ORM( Object Relationnel Mapping): • Productivité • Réduction sensiblement le temps de développement. • Maintenabilité • Utilisation d’un tampon entre les deux modèles pour isoler les modifications mineurs de chaque modèle. • Performance • Utilisation d’un grand nombre d’optimisation.
Solution ORM: Hibernate • Un framework Java de persistance • Permet de faire la correspondence des tables de base de données relationnelle avec des objets java simples (POJO ou «Plain Old Java Object»). • Une fois la correspondance entre les deux mondes définie, le programme Java peut manipuler toutes les données en utilisant que des JavaBean, masquant totalement la base de données sous-jacente et ses spécificités. • Le framework assure le remplissage de ces objets et la mise à jour de la base en se basant sur leur contenu. • XML constitue le format de description de la correspondance entre les tables relationnelles et les classes Java.
Historique de Hibernate • Novembre 2001: fondé par Gavin King. • Juin 2003: hibernate 2.0 solution complète d’ORM. • Août 2003: Intérêt du monde de l’entreprise grandissant. • Octobre 2003: Hibernate rejoint JBoss. • Juin 2004: Premier brouillon de la spec EJB3 inspiré fortement d’Hibernate. • Octobre 2005: NHibernate rejoint JBoss. • Décembre 2005: Hibernate 3.1.
Hibernate: Architecture • Hibernate utilise des API Java existantes dont JDBC, JTA (Java Transaction API) et JNDI (Java Naming and Dictory Interface). • JDBC fournit un niveau rudimentaire d’abstraction des fonctionnalités communes aux bases de données relationnelles, ce qui permet à pratiquement toutes les bases de données dotées d’un pilote JDBC d’être supportées par Hibernate. JNDI et JTA permettent à Hibernate d’être intégré avec des serveurs d’applications J2EE. Architecture multicouches d’Hibernate extrait du livre « Hibernate » de Gavin King et Christian Bauer.
Hibernate: Interfaces centrales • Grâce à ces interfaces, on peut stocker et récupérer des objets persistants et contrôler des transactions. • Interface Session • L’interface Session est l’interface principale utilisée pour les applications Hibernate. Les instances de session sont légères et peu coûteuses à créer et à détruire. • C’est un objet mono-threadé, à durée de vie courte, qui représente une conversation entre l'application et l'entrepôt de persistance. • L’interface session est quelquefois appelée « gestionnaire de persistance » car il s’agit également d’une interface pour les opérations liées à la persistance comme le stockage et la récupération des objets. • SessionFactory • L’application obtient des instances Session à partir d’une SessionFactory. • La sessionFactory est loin d’être légère. Elle est destinée à être partagée par de nombreux thread d’application. Il y a en général une unique SessionFactory pour l’application entière.
Hibernate: Interfaces centrales • Interface configuration: • L’objet Configuration est utilisé pour configurer et amorcer Hibernate. L’application utilise une instance configuration pour spécifier l’emplacement de mapping et de propriétés spécifiques à Hibernate, puis pour créer la SessionFactory. • Interfaces Query et Criteria: • L’interface Query permet de faire porter des requêtes sur la base de données et de contrôler la manière dont la requête est exécutée . • Une instance Query est utilisée pour lier les paramètres de requête, limiter le nombre de résultats retournés et enfin exécuter la requête. • Les instances Query sont légères et ne peuvent pas être utilisées en dehors de la Session qui les a crées. • L’interface Creteria est très similaire, elle permet de créer et d’exécuter des requêtes par critères orientés objet.
Hibernate: Interfaces centrales • Interface Transaction : • L’interface Transaction est une API optionnelle. • C’est un objet mono-threadé à vie courte utilisé par l'application pour définir une unité de travail. • Elle abstrait le code de l'application de l’implémentation sous-jacentes gérant les transactions qu'elles soient JDBC, JTA ou CORBA. • Une Session peut fournir plusieurs Transactions dans certains cas.
Configuration de Hibernate • La configuration d’Hibernate consiste à ajuster un certains nombre de paramètres qui permettent d’optimiser le comportement à l’exécution d’Hibernate. • Il y a trois méthodes qui permettent de configurer Hibernate: • En utilisant un fichier hibernate.properties. • En utilisant une instance java.util.properties de manière programmatique. • En utilisant un fichier de configuration XML.
Hibernate: hibernate.properties • Exemple de fichier hibernate.properties pour une source de données fournie par conteneur le nom JNDI de la source de données. Hibernate.connection.datasource= java:/comp/env/jdbc/actionDB Hibernate.transaction.factory-class=\net.sf.hibernate.transaction. JTATransactionFactory Hibernate.transaction.manager_lookup_class= \net.sf.hibernate. transaction.JBOSSTransactionManagerLookup Hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect Activation de l’intégration avec JTA Localisation duTransactionManager Utilisation du dialect SQL
Hibernate: hibernate.cfg.xml • Un fichier de configuration XML peut être utilisé pour configurer entièrement une SessionFactory. • A la différence de hibernate.properties qui ne contient que des paramètres de configuration, le fichier hibernate.cfg.xml peut également spécifier l’emplacement des documents de mapping. Configuration cfg=new configuration(); Cfg.addRessouce(« essai/exple.hbm.xml »); Cfg.setProperties(System.getProperties()); SessionFactory sessions=cfg.buildSessionFactory();
Hibernate: hibernate.cfg.xml <?xml version=‘1.0’ encoding=‘utf-8’?> <!DOCTYPE hibernate-configuration PUBLIC "-Hibernate/Hibernate Configuration DTD//EN " " http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd> <hibernate-configuration> <Session-factory name=" java:/hibernate/HibernateFactory "> <property name=" show_sql ">true</property> <property name="connection.datasource "> Java:/comp/env/jdbc/ActionDB </property> <property name=" dialect "> Net.sf.hibernate.dialect.PostgreSQLDialect </property> <property name=" transaction.manager_look_class"> Net.sf.hibernate.transaction.JBossTransactionManagerLookup </property> <mapping ressource=" auction/Item.hbm.xml"/> <mapping ressource=" auction/Category.hbm.xml"/> <mapping ressource=" auction/Bid.hbm.xml"/> </session-factory> </hibernate-configuration> Type de document L’attribut name Les propriétés Hibernate Documents de mapping
Hibernate: hibernate.cfg.xml • La déclaration du type de document est utilisée par le parseur XML pour valider ce document d’après la DTD de configuration d’Hibernate. • L’attribut facultatif name équivaut à la propriété hibernate.session_factory_name. Il est utilisé pour la liaison JNDI de la SessionFactory. • Les propriétés Hibernate peuvent être spécifiées sans le préfixe hibernate. Les noms et les valeurs de propriétés sont identiques aux propriétés de la configuration programmatique. • Les documents de mapping peuvent être spécifiés en tant que ressources d’application
Hibernate: hibernate.cfg.xml • A présent Hibernate est prêt à être initialiser en utilisant: • Mais, comment Hibernate savait-il où le fichier de configuration se trouvait? • Lorsque configure( ) est appelé, Hibernate cherche un fichier nommé Hibernate.cfg.xml dans le CLASSPATH. Si on souhaite utiliser un nom de fichier différent ou placer ce fichier de configuration dans un sous-repertoire, on n’a qu’ajouter un chemin à la méthode configure( ): SessionFactory sessions=new configuration( ) .configure( ).buildSessionFactory( ); SessionFactory sessions=new configuration( ) .configure( ‘‘/hibernate-config/auction.cfg.xml’’) .buildSessionFactory( );
Langage de requête d’Hibernate : HQL • Fonctionnalités: • La capacité à appliquer des restrictions aux propriétés d’objets associés liés par référence. • La capacité à trier les résultats de la requête • La capacité à paginer les résultats • L’agrégation avec group by, having et des fonctions d’agrégation comme sum, min et max. • Les jointures externes lors de la récupération de plusieurs objets par ligne
Langage de requête d’Hibernate : HQL • La capacité à appeler des fonctions SQL définies par l’utilisateur. • Les sous requêtes (requêtes imbriqués). • HQL est moins verbeux et plus lisible que la requête avec SQL. • Exemple de requête avec SQL: Le même en HQL
Hibernate: SQL natif • L'un des objectifs d'Hibernate est de s'éloigner de la base de données. Or, l'utilisation du SQL natif va à l'encontre de cet objectif. De ce point de vue, le SQL natif est à éviter. • Néanmoins, l'équipe du projet le conseille seulement de façon exceptionnelle pour des points qui ont besoin d'utiliser une optimisation spécifique à la base de données. • Cette fonctionnalité offre un moyen de migration plus propre d'une application basée sur SQL/JDBC vers une application Hibernate.
Un peu d’histoire … Entity Beans EJB 1.0 -> 2.1 Complexité La courbe d’apprentissage trop prononcée Modèle de programmation non-intuitif Performance Le coût pour effectuer certaines requêtes était hors de proportions, le cycle de vie du container devenait un boulet
Un peu d’histoire … Les équipes IT se sont de plus en plus tourné vers d’autres produits sur le marché: Hibernate Le plus répandu dans la communauté présentement. Toutefois, la responsabilité de compatibilité avec différents serveurs d’application revient à la communauté. JDO Bien que JDO soit un standard, appuyé par la JSR 243 (JDO 2.0), cette technologie a reçu bien peu d’appui. Produits commerciaux (ex. TopLink) API propriétaires, onéreux, non-standard … Ces trois solutions fonctionnent globalement suivant les mêmes principes : Des fichiers de mapping pour faire correspondre les tables aux classes java Des beans Java simples correspondant aux objets métiers : ceux sont ces beans que manipule le développeur Une API de persistance qui permet de rendre persistant en base les beans métiers. On retrouve généralement les mêmes verbes (insert, update, delete, get) Un langage de requêtes proche du SQL mais manipulant les propriétés des beans.
Les besoins Clairement, 2 grands besoins se dégagèrent, répondant à l’industrie et aux développeurs: Avoir une API simple d’utilisation, attrayante pour les développeurs, appliquant les techniques apprises des cadres d’applications de la communauté open source. Avoir un standard respecté par les fournisseurs de serveurs d’applications.
L'orientation EJB 3.0 Les spécifications des EJB 3.0 Entité et de Java Persistence API (JPA) reprennent les grands mécanismes des solutions de mapping relationnel/objet. Les différents fournisseurs ont décliné leur offre afin d’intégrer les spécifications des EJB 3.0 Entité et JPA : Hibernate supporte les annotations EJB 3.0 et JPA : Hibernate 3.2 peut être utilisé comme avant, ou bien servir d’implémentation aux EJB 3.0. Toplink est l’implémentation des EJB 3.0 Entité pour la solution Oracle On assiste donc semble-t-il à la fin du casse-tête du choix d’architecture de la couche de persistance dans le monde Java : nous avons une interface normée qui est supporté par les acteurs principaux et qui repose sur des solutions et concepts déjà éprouvés.
Présentation de JPA JPA est un standard pour la persistance des objets Java L'API JPA est récente. Elle n'a été disponible qu'à partir du JDK 1.6. L'API de persistance est accessible en utilisant le paquetage Java javax.persistence. La spécification JPA a été implémentée par les deux produits Toplink et Hibernate.
Mapping Objet Relationnel La communication entre les mondes objet et relationnel suppose une transformation pour adapter la structure des données relationnelles au modèle objet.
Fichier de configuration Un seul fichier de configuration XML Il est nécessaire d’indiquer au fournisseur de persistance comment il peut se connecter à la base de données Ce fichier peut aussi comporter d’autres informations Fichier de configuration
Fichier de configuration Déclaration du l’unité persistante <persistence <persistence-unit name="ExpoJPA"> <provider> oracle.toplink.essentials.PersistenceProvider </provider> <mapping-file> META-INF/orm.xml </mapping-file> <class> com.expose.jpa.Personne </class> <properties> <property name="toplink.jdbc.driver“ value="com.mysql.jdbc.Driver" > </property> <property name="toplink.jdbc.url” value="jdbc:mysql://localhost:3306/expojpa"> </property> <property name="toplink.jdbc.user" value="root"> </property> </properties> </persistence-unit> </persistence> configurent la liaison JDBC avec la base de données
Les Entités Les classes dont les instances peuvent être persistantes sont appelées des entités dans la spécification de JPA Le développeur indique qu’une classe est une entité en lui associant l’annotation @Entity Il faut importer javax.Persistence.Entity dans les classes entités.
Classes entités : Entity Class Une classe entité doit posséder un attribut qui représente la clé primaire dans la BD (@Id) Elle doit avoir un constructeur sans paramètre protected ou public Elle ne doit pas être final Aucune méthode ou champ persistant ne doit être final Une entité peut être une classe abstraite mais elle ne peut pas être une interface
Classes entités : Embedded Class Il existe des classes incorporées (embedded) dont les données n’ont pas d’identité dans la BD mais sont insérées dans une des tables associées à une entité persistante Par exemple, une classe Adresse dont les valeurs sont insérées dans la table Employe
EntityManager Classe javax.persistence.EntityManager Le gestionnaire d’entités est l’interlocuteur principal pour le développeur. Il fournit les méthodes pour gérer les entités : les rendre persistantes, les supprimer de la base de données, retrouver leurs valeurs dans la base, etc.
Cyclede vie d’un EntityManager La méthode createEntityManager() de la classe EntityManagerFactory créé un Entity Manager L’Entity Manager est supprimé avec la méthode close() de la classe EntityManager, il ne sera plus possible de l’utiliser ensuite.
Fabrique de l’EntityManager La classe Persistence permet d’obtenir une fabrique de gestionnaire d’entités par la méthode createEntityManagerFactory 2 variantes surchargées de cette méthode : 1 seul paramètre qui donne le nom de l’unité de persistance (définie dans le fichier persistence.xml) Un 2ème paramètre de type Map qui contient des valeurs qui vont écraser les propriétés par défaut contenues dans persistence.xml
Méthodes de EntityManager void flush() Toutes les modifications effectuées sur les entités du contexte de persistance gérées par l’EntityManager sont enregistrées dans la BD lors d’un flush du GE Au moment du flush, l’EntityMnager étudie ce qu’il doit faire pour chacune des entités qu’il gère et il lance les commandes SQL adaptées pour modifier la base de données (INSERT, UPDATE ou DELETE) void persist(Object entité) Une entité nouvelle devient une entité gérée, l’état de l’entité sera sauvegardé dans la BD au prochain flush ou commit void remove(Object entité) Une entité gérée devient supprimée, les données correspondantes seront supprimées de la BD
Méthodes de EntityManager void lock(Object entité, LockModeType lockMode) Le fournisseur de persistance gère les accès concurrents aux données de la BD représentées par les entités avec une stratégie optimiste, lock permet de modifier la manière de gérerles accès concurrents à une entité void refresh(Object entité) L’EntityManager peut synchroniser avec la BD une entité qu’il gère en rafraichissant son état en mémoire avec les données actuellement dans la BD. Utiliser cette méthode pour s’assurer que l’entité a les mêmes données que la BD <T> T find(Class<T> classeEntité,Object cléPrimaire) La recherche est polymorphe : l'entité récupérée peut être de la classe passée en paramètre ou d'une sous-classe (renvoie null si aucune entité n’a l’identificateur passé en paramètre)
Relations 4 types de relations à définir entre les entités de la JPA: One to One Many to One One to Many Many to Many
Relationship: Many to One @Entity @Entity @Table(name="EMP") public class Department { private int id; private String dname; // getters & setters ... } public class Employee { private int id; privateDepartment d; // getters & setters ... } @Id @Id @ManyToOne @JoinColumn(name="DEPT_ID") DEPARTMENT EMP ID DNAME ID DEPT_ID PK PK FK
Relationship: One to Many @Entity @Entity @Table(name="EMP") public class Department { private int id; private String dname; privateCollection<Employee> emps; // getters & setters ... } public class Employee { private int id; privateDepartment d; // getters & setters ... } @Id @Id @ManyToOne @OneToMany(mappedBy="d") @JoinColumn(name="DEPT_ID") DEPARTMENT EMP ID DNAME ID DEPT_ID PK PK FK
Relationships: One to One @Entity @Entity @Table(name="EMP") public class ParkingSpace { private int id; private int lot; private String location; privateEmployee emp; // getters & setters ... } public class Employee { private int id; privateParkingSpace space; // getters & setters ... } @Id @Id @OneToOne @JoinColumn(name="P_SPACE") @OneToOne(mappedBy="space") PARKINGSPACE EMP ID LOT LOCATION ID P_SPACE PK PK FK
Relationships: Many to Many @Entity @Entity @Table(name="EMP") public class Project { private int id; private String name; privateCollection<Employee> e; // getters & setters ... } public class Employee { private int id; privateCollection<Project> p; } @Id @Id @ManyToMany(mappedBy="p") @JoinTable(name="EMP_PROJ", joinColumns= @JoinColumn(name="EMP_ID"), inverseJoinColumns= @JoinColumn(name="PROJ_ID")) @ManyToMany EMP_PROJ PROJECT EMP EMP_ID PROJ_ID ID NAME NAME SALARY ID PK PK PK,FK1 PK,FK2
Étapes pour récupérer des données Il est possible de rechercher des données sur des critères plus complexes que la simple identité Les étapes sont alors les suivantes : Décrire ce qui est recherché (langage JPQL) Créer une instance de type Query Initialiser la requête (paramètres, pagination) Lancer l’exécution de la requête La JPA introduit le JPQL (Java Persistence Query Language), qui est, tout comme le EJBQL ou encore le HQL, un langage de requête du modèle objet, basé sur SQL. Queries
Interface Query Représente une requête Une instance de Query (d’une classe implémentant Query) est obtenue par les méthodes createQuery, createNativeQuery ou createNamedQuery de l’interface EntityManager
Queries: NamedQueries Peut être mise dans n’importe quelle entité, mais on choisira le plus souvent l’entité qui correspond à ce qui est renvoyé par la requête On peut sauvegarder des gabarits de requête dans nos entités. Ceci permet : La réutilisation de la requête D’externaliser les requête du code. @Entity @NamedQuery(name="myQuery", query="Select o from MyPojo o") public classMyPojo { … } public classMyService { public voidmyMethod() {… List results = em.createNamedQuery("myQuery").getResultList();… }}
Queries: NativeQueries Une façon de faire des requête en SQL natif. Sert principalement à avoir plus de contrôle sur les requêtes à la base de donnée. public classMyService { public voidmyMethod() { … List results = em.createNativeQuery(“SELECT * FROM MyPojo“, MyPojo.class) .getResultList(); … } }