Object Relational Mapping - Java Persistence API

Java Persistence API, les aspects techniques (1/2)

Composition

J.P.A est composé de cinq parties :

L'unité de persistance

Il s'agit de l'environnement dans lequel la couche de persistance va être utilisée. Il est ainsi nécessaire de spécifier les éléments suivants :

L'ensemble de cette configuration s'effectue à l'aide d'un fichier XML. On notera cependant une différence entre les architectures 2-Tiers et 3-Tiers (avec conteneur d'EJB3). En effet on déportera une partie de la configuration sur le serveur d'application dans le cas d'une architecture 3-Tiers, dans le but de faciliter l'administration et d'offrir plus de souplesse.

Cas d'une architecture 2-Tiers

Voici un exemple de fichier de configuration pour une architecture 2-Tiers :

<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
        <persistence-unit name="xposejpa-javadb">
                <provider>oracle.toplink.essentials.PersistenceProvider</provider>
                <class>fr.umlv.etudiant.acollign.jpa.bom.Person</class>
                ...
                <properties>
                        <property name="toplink.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver" />
                        <property name="toplink.jdbc.url" value="jdbc:derby://localhost/xpose" />
                        <property name="toplink.jdbc.user" value="xpose" />
                        <property name="toplink.jdbc.password" value="china" />
                        <property name="toplink.ddl-generation" value="create-tables" />
                </properties>
                </persistence-unit>
</persistence>
                 

Le lecteur attentif aura remarqué que les noms des propriétés présentées ici sont suffixées de toplink. Il est important de noter que les noms des propriétés sont propres aux implémentations.

Il est ainsi possible de faire cohabiter les configurations pour différentes implémentations pour une même unité de persistance.

Cas d'une architecture 3-Tiers

Voici un exemple de fichier de configuration pour une architecture 3-Tiers :

<persistence version="1.0">
     <persistence-unit name="xpose-jpa">
           <jta-data-source>jdbc/xpose</jta-data-source>
     </persistence-unit>
</persistence> 
                 

On constate que la configuration est bien plus légère dans ce fichier. Cela s'explique par le fait que l'ensemble de la configuration de la base de données est effectué dans la datasource.

Une datasource correspond à une connexion ou un ensemble de connexions (pool) configuré sur le serveur d'application. Elle a ainsi l'avantage d'être partagée par un ensemble d'applications entreprises.

Les objets du domaine, les entités

Définition

Les objets du domaine correspondent à l'ensemble des objets métiers que l'application traite. Ainsi dans l'exemple on aura les objets suivants :

Les entités

Les entités sont la simple traduction Java et plus précisément J.P.A des objets du domaine sus-cité. On trouvera ainsi quatre entités pour notre exemple :

L'entité Person n'est pas définie clairement dans les objets du domaine, cependant sa création permet de tirer pleinement partie de la puissance de la programmation objet.

Un Javabean

Pour créer une entité, il faut dans un premier temps écrire un objet JavaBean. On trouvera souvent dans la littérature le nom de Plain Old Java Object ou son acronyme P.O.J.O. Pour rappel un JavaBean possède les caractéristiques suivantes :

La gestion des événements n'est pas requise dans le cadre de J.P.A.

Voici le P.O.J.O correspondant à l'objet du domaine Person :

public class Person {
     private  String lastname;
     private  String firstname;
     private  Address address;
     public Person() {}
     public Person(String lastname, String firstname, Address address) {
           this.firstname = firstname;
           this.lastname = lastname;
           this.address = address;
     }
     public String getLastname() {
           return lastname;
     }
     public void setLastname(String lastname) {
           this.lastname = lastname;
     }
     ...
}                        
                
Une clé primaire

Pour identifier notre entité au sein de la base de données, il est obligatoire de définir un identifiant unique, la clé primaire. La clé primaire peut être de deux formes différentes :

Une fois la clé primaire définie, il est nécessaire de mettre en place la génération de cette dernière. Pour cela, la norme J.P.A dispose d'un certain nombre de règles :

Les deux méthodes SEQUENCE et IDENTITY ne sont pas toujours portables et peuvent ainsi nuire à la migration et l'évolution des entités et de l'application.

Les méta-données

Les méta-données permettent de réaliser l'association entre les membres des objets et leurs représentations au sein de la base de données relationnelles. Il s'agit de la mise en place d'un mapping à proprement parler.

La spécification de J.P.A étant basée sur la version 5 de la plate-forme Java, on trouve naturellement l'usage des @Annotations dans la description des méta-données. On pourra cependant préférer l'usage d'un fichier de description XML.

Usage des annotations

On va donc augmenter notre code d'un ensemble d'annotations. Il existe de nombreuses annotations dont voici les plus couramment utilisées :

Voici un exemple de mise en place des méta-données par annotation sur la classe Person :

@Entity
public class Person {
     @Id
     @GeneratedValue
     private long id;
     @Column(name="LASTNAME")
     private String lastname;
     private String firstname;
     private Address address;
     public Person() {}
     public String getLastname() { return lastname;}
     public void setLastname(String lastname){
           this.lastname = lastname;
     }
     ...
}
                
Usage du fichier orm.xml

Une autre approche consiste à utiliser un fichier de description XML ; cette approche est celle utilisée dans la norme EJB2 qui est basée sur Java 1.4 :

<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings version="1.0">
     <entity class="fr.umlv.etudiant.acollign.jpa.bom.Person">
           <attributes>
                <id name="id">
                      <generated-value />
                </id>
                <basic name="lastname">
                      <column name="LASTNAME" />
                </basic>
           </attributes>
     </entity>
</entity-mappings>
                
Usage simultanné des deux méthodes

Les deux méthodes peuvent être utilisées simultanément, cette technique s'appelle le merged meta-data. Cependant, une telle utilisation peut apporter certaines confusions. En effet, le développeur plongé dans son code, peut croire en la configuration définie par les annotations alors que celles-ci sont potentiellement écrasées par un fichier de description annexe.

On notera une utilisation courante qui est de définir l'environnement de développement par les annotations puis lors du passage en production, une réécriture de la configuration est effecutée grâce au fichier de description.

On peut constater dans l'exemple donné ci-dessus que tous les champs ne sont pas annotés. En l'absence de méta-données, un comportement par défaut est appliqué. Ce principe s'applique à la fois aux paramètres optionnels et aux annotations.

Pour exemple, le mapping d'un champ non renseigné se fera sur la colonne ayant le même nom que le champ lui même.

la suite...