Concept des ORM
Introduction
Le mapping objet-relationnel (Object-Relational Mapping en anglais) est une technique de programmation qui permet l’abstraction d’une base de données relationnelle afin de simuler une base de données orientée objet.
Le but des ORM est de faciliter la manipulation des différents objets stockés dans une base de données relationnelle au sein d’un langage de programmation orienté objet. Ainsi, le développeur ne manipule que des objets, la partie persistance est gérée par l’ORM.
Le diagramme suivant montre qu’un développeur peut uniquement intervenir sur les objets et que l’ORM se charge de répercuter les différentes modifications sur le Système de Gestion de Base de Données (SGBD).
Le concept
De ce fait, vers la fin des années 1990, les premières versions d’outils permettant d’automatiser la récupération des données sans avoir à interroger directement les bases de données ont vu le jour. Java, avec les EJB (Entreprise Java Bean) et l’API EJB Entity 1.0, n’a pas connu le succès attendu, dû au fait que l’API ne répondait pas complètement à la problématique posée, sa complexité ainsi que ses faibles performances sont probablement en cause. Par contre, cette API est l’élément déclencheur de solution ORM open-source, car en parallèle quelques solutions propriétaires voient le jour telles que Toplink, qui existe toujours sous le nom d’EclipseLink.
Au début des années 2000, Hibernate fait son apparition. Étre open source, gratuit et de qualité industrielle lui permet un succès immédiat sur le marché des ORM et il devient l’un des principaux acteurs du milieu.
Suite au succès des différents ORM, Sun décide de créer un standard JEE : JPA (Java Persistent API).
1. La norme : JPA
Comme il existe plusieurs solutions d’ORM, payantes ou gratuites, open source ou non, chacune à sa propre règle de nommage, son langage de paramétrage particulier…
Pourtant le principe de base...
La structuration des données
1. Introduction au mapping
D’une part, une base de données stocke les informations sous forme de tables, colonnes, contraintes et clés étrangères. De l’autre, une application développée en Programmation Orientée Objet (POO) utilise différents objets. Afin de pouvoir faire dialoguer automatiquement ces deux couches dans le système d’information, il faut définir les relations/interactions entre les données et le modèle objet, ce qui s’effectue via le mapping.
2. Importance du mapping
Comme la conception d’une base de données, le mapping objet-relationnel est une phase importante. Suivant le besoin fonctionnel, sa réalisation peut être différente. Ainsi, le choix de l’objet propriétaire de la relation, du moment où sera réalisée la jointure, du type de donnée retourné… va influer directement sur les performances de l’ORM.
Un bon mapping fait gagner du temps autant à la réalisation de l’application ou à son maintien que lors de son exécution.
3. Différentes techniques de mapping
Le mapping peut être réalisé de deux façons différentes :
En utilisant des fichiers de configuration, qui sont écrits en XML. Cette approche permet d’avoir dans un seul fichier externe...
La connexion aux données
1. Introduction aux sessions
Une fois le mapping de l’ORM réalisé, pour l’utiliser il faut pouvoir se connecter à la base de données. Lorsque l’application n’utilise pas d’ORM, la plupart des connexions s’effectuent via l’API JDBC (Java DataBase Connectivity) en utilisant le DriverManager pour récupérer une Connection.
Par exemple, une connexion à une base MySQL avec cette API s’effectue de la manière suivante :
//Enregistrement du pilote JDBC
Class.forName("com.mysql.jdbc.Driver");
//Récupération d'une connexion
Connection conn = DriverManager.getConnection(DB_URL,USER,PASS);
JPA, lui, a besoin d’un EntityManagerFactory qui va connaître les paramètres de connexion et d’un EntityManager qui établit la connexion à la base de données. En simplifiant la comparaison entre JDBC et JPA, l’EntityManagerFactory a le rôle du DriverManager tandis que l’EntityManager a celui de la Connection.
L’EntityManagerFactory est l’objet qui connaît le mapping objet-relationnel et les paramètres pour se connecter à la base de données (tels que l’URL, le login, le mot de passe…). Ces données sont chargées via le fichier persistence.xml qui est décrit dans la section Paramétrage de l’ORM du chapitre Préparation d’un projet. Ainsi, il est fortement conseillé de n’avoir qu’une seule instance d’EntityManagerFactory qui est créée au démarrage de l’application et supprimée à l’arrêt de l’application.
Par contre, comme l’EntityManager représente la connexion avec la base de données, il peut être créé, ouvert puis fermé au besoin. C’est avec lui que les différentes transactions et/ou requêtes sont effectuées.
Toutes ces informations sont configurables dans un seul fichier qui sert à...
L’interrogation des données
1. Introduction aux requêtes
Une fois le mapping réalisé et la connexion établie, il n’y a plus qu’à manipuler les données grâce à l’habituel CRUD (Create Read Update Delete). Pour cela, JPA met plusieurs outils à disposition.
Le plus connu étant le langage SQL (Structured Query Language, en français langage de requête structurée). C’est le langage normalisé d’exploitation des bases de données relationnelles. JPA prévoit nativement l’utilisation de requêtes SQL.
Ensuite, comme les ORM travaillent avec un modèle objet, JPA a mis en place un langage de requête objet : Java Persistence Query Language (JPQL), fortement inspiré du langage de requête d’Hibernate : Hibernate Query Langage (HQL).
Il y a ensuite les requêtes nommées qui permettent de sauvegarder des requêtes pour qu’elles soient utilisées plus facilement.
Il y a aussi ce qu’on appelle des requêtes "implicites" qui s’exécutent automatiquement lors de la navigation entre des objets via les getter.
Et finalement, comme tous les exemples vus précédemment se font via des requêtes écrites sous forme de chaînes de caractères, JPA a mis en place l’API Criteria qui permet, via la construction d’objets, d’interroger la base de données.
Toutes ces requêtes s’exécutent depuis l’EntityManager.
2. Les requêtes natives
Comme leur nom l’indique, ces requêtes sont natives par rapport au système de gestion de base de données. C’est-à-dire que toutes les requêtes qui peuvent être exécutées par le SGBD peuvent l’être via l’EntityManager et sa méthode createNativeQuery.
Ci-dessous, un exemple de requête native exécutée via JPA :
EntityManage em = ...
Query q = em.createNativeQuery("SELECT p.id,p.nom "
+ "FROM Personne p "
...
Le cycle de vie des données
1. Introduction au cache
Grâce à la structuration des données et à l’interrogation des données mises en place par les ORM, la maintenance se trouve facilitée et cela entraîne des gains de productivité non négligeables. Mais si les ORM ne prennent pas en compte la gestion du cycle de vie des données en mémoire, les performances peuvent très vite en souffrir. En effet, comme les entités sont directement liées à la base de données, il est légitime de se demander à quel moment l’interrogation de la base de données s’effectue et si cela se fait en permanence.
Afin d’optimiser les accès à la base de données, les ORM ont leur propre espace mémoire pour stocker temporairement les données : les caches. C’est-à-dire que toute opération se fait principalement sur la mémoire de l’ORM et que ce dernier synchronise ses données à certains moments.
Une explication simplifiée de leur mode de fonctionnement est que, lorsque l’application veut récupérer des données, l’ORM vérifie dans un premier temps dans son cache, et s’il ne trouve pas les informations, il interroge la base de données. Les modifications sont faites au fur et à mesure dans le cache et transmises...