Tutoriel sur l'utilisation de JDO sur une base de données relationnelle

Benoit Moussaud

Après une brève introduction à JDO, je vous propose de mettre en œuvre cette technologie sur une base de données MySQL en deux exemples : un simple démontrant les capacités primaires de JDO, un complexe démontrant principalement les mécanismes de requêtes. ♪

- Titre : Tutoriel sur l'utilisation de JDO sur une base de données relationnelle

- Auteur : Benoit Moussaud

- Parution : 20 octobre 2004

- Mise à jour : 10 mai 2005

- Licence : Copyright ® 2005 Benoit Moussaud. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. QU'EST-CE QUE JDO ?

- I-A. Définition

- I-B. Architecture

- I-C. Cycle de développement

- II. INSTALLATION DES COMPOSANTS

- II-A. MySQL

- II-B. Java

- II-C. Ant

- II-D. Lido XD

- II-E. JPox

- III. FONCTIONS DE BASE

- III-A. Le modèle

- III-B. La persistance

- III-C. Chargement d'objets

- III-D. Modification

- III-E. Suppression

- IV. FONCTIONS AVANCÉES

- IV-A. Le modèle

- IV-B. metadata.jdo

- IV-C. Création de données

- IV-D. Requêtes

- IV-D-1. Vérification du nombre d'instances

- IV-D-2. Les Tris

- IV-D-3. Requêtes avec paramètres

- IV-D-4. Requêtes avec variables

- V. LE FUTUR

- V-A. Fonctions avancées

- V-B. JDO 2.0

- V-C. Liens utiles

- V-D. Crédits et Licence

I - QU'EST-CE QUE JDO ?

I-A - Définition

JDO est une spécification définissant le concept de 'persistance transparente' pour un objet Java vers une source de données transactionnelle. Exemple de source de données : Base de données relationnelle, Fichier XML, base de données objet, mainframe…Donc JDO est bien plus qu'un outil de mapping objet/relationnel, c'est un outil de persistance. JDO est conduit par le JSR 12 (Java Specification Request).

- Modélisation objet sans contraintes : supporte l'héritage, les collections, les interfaces…

- Persistance transparente : permet d'avoir des objets métier complètement indépendants de la source de données.

- Réduction des temps de développement. En effet toute la partie d'accès à la base qui représente environ 40 % des temps de développement ( !!) est prise en charge par JDO.

- Isole les compétences métier des compétences base de données des équipes de développement. Il n'est plus nécessaire d'avoir un DBA ou expert base de données à plein temps, pour les projets simples à moyens. Ce type de projet doit représenter facilement 2/3 des projets informatiques.

- Persistance universelle : JDBC est limitée au SGBDR, JDO est capable, au déploiement, d'assurer la persistance autant dans un SGBDR qu'un SGBDO, un système type CICS, Fichier plat ou XML.

- JDO est disponible sur toutes versions de Java: J2SE, J2ME et J2EE Architecture client/serveur ou multitiers

De cette vision idéaliste, beaucoup s'élèveront pour dire que c'est faux. Personnellement je dirais que c'est globalement vrai. Il existe beaucoup d'applications, les applications de Gestion classiques (3Tiers sans grosses contraintes) où JDO apporte un vrai gain. Les drivers génèrent du SQL parfois mieux que les développeurs (souvent débutants) eux-mêmes. Dans les applications plus critiques ou moins standards, les drivers JDO permettent soit de prendre la main (récupérer la Datasource et effectuer la requête à la main avec l'aide de son DBA) soit d'utiliser des extensions dites propriétaires échappant à la spécification, mais remplissant les vides laissés par le JSR-12. Comme toute technologie, il faut employer JDO à bon escient dans les cas prévus pour.

I-B - Architecture

JDO se place entre les objets métier et les API d'accès aux sources de données : JDBC pour les bases de données relationnelles, SAX/DOM pour les accès XML… Ces objets ne font référence qu'à quelques interfaces définies par JDO. Ils sont donc complètement indépendants de la source de données utilisée.

I-C - Cycle de développement

Le développement d'un logiciel utilisant JDO n'est pas différent du cycle de développement traditionnel en Java. Il ajoute juste une simple étape : l'enhancement. Cette étape d'amélioration du byte code, amélioration indépendante de la source de données, va permette au driver (et à lui seul) de pouvoir accéder à l'ensemble des attributs persistants de l'objet, même les attributs privés. Et ceci afin de rendre persistantes et de pouvoir persister l'ensemble des classes Java.
Cette étape se situe juste après la compilation, comme le montre ce schéma :

L'Enhancement est complètement défini par la spécification…

- Le byte-code enhancé est portable entre les implémentations JDO.

- Le byte-code enhancé est le même pour n'importe quelle source de données.

- Permet toujours de débogage (JPDA).

Et assure une persistance complètement transparente.

II - INSTALLATION DES COMPOSANTS

Dans notre tutoriel nous allons faire persister nos classes Java dans une base de données MySQL en utilisant le driver Lido édité par la société XCalia.

II-A - MySQL

MySQL est l'un des moteurs de base de données « open source » le plus utilisé. Il est disponible autant sous Windows que Linux.

Installation du Moteur
MySQL est disponible gratuitement en téléchargement sur le site mysql.com. Dans ce tutoriel nous utilisons la version 4.0.20d. Sous Windows, l'installation se fait avec un programme d'installation.
Nous installons MySQL dans le répertoire : C:\tutoriel\mysql en mode typical.
Éditez ou créez le fichier c:/windows/my.ini avec les informations suivantes :

mysql.ini

[mysqld]
basedir=C:/tutoriel/mysql
datadir=C:/tutoriel/mysql/data
language=C:/tutoriel/mysql/share/french
port=3306

Allez dans le répertoire C:/tutoriel/mysql/bin et lancez mysqld-max-nt install pour installer le moteur en tant que service NT. Puis lancez la commander NET START mysql pour démarrer le moteur.

Drivers
Pour notre étude nous allons avoir besoin du driver jdbc de mysql. Le driver JDBC, Connector/J, est disponible sur le site de MySQL: Télécharger.
Extraiez le fichier .zip et copiez le fichier mysql-connector-java-3.0.14-production-bin.jar sous c:/Tutoriel/myslq/jdbc (répertoire à créer).

II-B - Java

Notre tutoriel a besoin d'un JDK version 1.4.x. Positionnez la variable d'environnement JAVA_HOME sur votre JDK

Ex : JAVA_HOME= D:\DevTools\j2sdk1.4.2_04

Et ajoutez au PATH le répertoire %JAVA_HOME%\bin.

PATH=%JAVA_HOME%\bin;%PATH%

II-C - Ant

ANT est un équivalent de make pour le monde Java qui est devenu l'outil de construction de la plupart des projets open source. Il nous servira à compiler, enhancer et lancer nos exécutables Java.

- Téléchargez ANT en version 1.6.x sur le site de jakarta

- Extraiez l'archive sous C:\tutoriel\apache-ant-1.6.2.

- Déclarez une variable d'environnement ANT_HOME pointant sur le répertoire ANT : ANT_HOME = C:\tutoriel\apache-ant-1.6.2.

- Ajoutez au path le chemin %ANT_HOME%\bin: PATH=%ANT_HOME%\bin;%PATH%.

- Ouvrez une console et vérifiez que ANT est bien trouvé :

C:\ ant -version
Apache Ant version 1.6.2 compiled on July 16 2004

II-D - Lido XD

Lido XD est une implémentation commerciale de JDO produite par la société Xcalia LiDO XD est gratuit pour l'accès permanent aux bases open source, hors J2EE. La connexion est limitée à 30 mn pour les bases de données commerciales ou/et l'utilisation avec un serveur d'application J2EE. Toutes les fonctionnalités de LiDO XD sont incluses dans la licence gratuite de développement, valable six mois et renouvelable par téléchargement, cependant celles permettant un déploiement commercial sont limitées à 30 mn. Sur leur site, il est nécessaire de vous identifier avant de télécharger une version d'évaluation.

L'installation de Lido se fait en lançant sous Windows par un programme de setup et sous les autres environnements par la commande :java -jar lido_xd_x.y.jar ou en double cliquant sur le jar si votre JDK est configuré pour cela. Nous installons le drivers et tous ses fichiers dans : c:\tutoriel\lido. Positionnez la variable d'environnement LIDO_HOME sur le répertoire d'installation. LIDO_HOME=C:\tutoriel\lido, set LIDO_BIN=%LIDO_HOME%\bin et si vous avez des problèmes d'enhancement avec Lido: SET ANT_HOME=%LIDO_BIN%\ant16 et SET PATH=« %ANT_HOME%\bin »;%PATH%

Si vous avez encore des problèmes lors du lancement de la commande : ant enhance, lancez le script suivant :

"%LIDO_HOME%\bin\lidoEnv.bat" noclasspath

II-E - JPox

JPox est une implémentation sous licence Open Source Apache 2 de la spécification JDO. Pour le moment, cette équipe se concentre à une persistance vers les bases de données relationnelles tandis que Lido adresse un plus large éventail de sources (SGBDR,XML,SGBDO…). JPox a été sélectionné par Sun pour être l'implémentation de référence de la spécification JDO 2.0, au même titre que Tomcat est l'implémentation de référence pour la technologie JSP/Servlet.

Les différents exemples pour JPox contiendront les drivers jpox nécessaires à les faire fonctionner. Les principales différences entre les exemples pour Lido et Jpox proviennent essentiellement du fichier de mapping et du moyen d'accéder au PersistenceManager.

Voilà l'ensemble de nos composants est installé, nous allons pouvoir faire persister des objets !

III - FONCTIONS DE BASE

Nous allons commencer par un petit exemple simple afin de voir les différentes étapes.
L'archive Simple contient les sources, le script ANT, les fichiers de configuration et les programmes de test (Workspace Eclipse 3.0).

- Téléchargez Simple Version Lido

- Téléchargez Simple Version Jpox

III-A - Le modèle

Pour notre premier exemple, nous allons faire persister une simple classe : la classe Personne avec quatre attributs : nom, prénom, date de naissance et un identificateur.

Personne.java

package org.moussaud.data;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Personne {
private String nom;
private String prenom;
private Date   datedenaissance;
private int    secuid;
// constructor
// getter (pas de setter sauf setSecuId)
public String toString() {
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
return getNom() + " "+getPrenom()+ " Date de naissance: "+formatter.format(getDatedenaissance());
}
}

Le programme de test qui va avec :

Main.java

public static void main(String[] args) throws Exception {
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
Personne p = new Personne("Moussaud", "Benoit", formatter.parse("07.05.1973"), 12345);
System.out.println(p);
System.out.println("SecuId: "+p.getSecuid());
}

Pour voir l'exécution, placez-vous dans le répertoire simple et lancez :

ant compile
compile:
[javac] Compiling 2 source files to C:\tutoriel\simple\bin

ant run.test1
run.test1:
[java] Moussaud Benoit Date de naissance: 07/05/1973
[java] SecuId: 12345

Rien de bien compliqué dans ce code !

III-B - La persistance

Avant de rendre une classe persistante avec JDO, il faut déclarer dans un fichier xml – généralement nommé metadata.jdo – quelles classes et quels attributs de la classe sont persistants.
Dans notre cas c'est très simple :

metadata.jdo

<?xml version="1.0" encoding="UTF-8"?>
<jdo>
<package name="org.moussaud.data">
<class name="Personne" identity-type="datastore">
<field name="nom"/>
<field name="prenom"/>
<field name="datedenaissance"/>
<field name="secuid"/>
</class>
</package>
</jdo>

III-C - Chargement d'objets

Maintenant que l'on a une instance dans notre base de données, il peut être intéressant de la charger. Nous allons ici parler de chargement, un paragraphe sur les requêtes est disponible par la suite. Le chargement de toutes les instances d'une classe donnée se fait à travers l'utilisation de l'interface Extent. Une instance du PersistentManager peut renvoyer un Extent sur une classe par la méthode getExtent(Class, boolean). Le programme de test montre l'utilisation de cette interface. (La partie initialisation a été supprimée, c'est la même que dans le test2) :

chargement

System.out.println("Extent sur la classe Personne");
Extent e = pm.getExtent(Personne.class,true);
System.out.println("Iteration");
Iterator it = e.iterator();
while (it.hasNext()) {
Personne p = (Personne) it.next();
System.out.println(p);
System.out.println("SecuId: " + p.getSeuid());
//Affichage de l'objet id
System.out.println("OID   : " + pm.getObjectId(p));
}

À travers l'itérator, le driver JDO permet d'accéder à toutes les instances de la classe Personne. Dans notre exemple, en plus de récupérer les informations propres à l'instance Personne, on affiche l'object id (attribué par le driver) à notre instance. Il possible de retrouver une instance à partir de son object id en utilisant la méthode : Pm.getObjetcById(Object oid, boolean b);

exemple

Object oid = pm.getObjectId(p) ;
Personne p2 = (Personne) pm.getObjetcById(oid,true) ;

Le lancement du programme de test Test3 donne la sortie suivante :

Ant run.test3
run.test3:
[java] Extent sur la class Personne
[java] Iteration
[java] Moussaud Benoit Date de naissance: 07/05/1973
[java] SecuId: 12345
[java] OID   : 2

III-D - Modification

Une fois un objet chargé, on peut le modifier comme n'importe quel objet Java. Le programme de test Test4 va procéder au chargement de toutes les instances de Personne et va incrémenter le secuid.

modification

tx.begin();

System.out.println("Extent sur la class Personne");
Extent e = pm.getExtent(Personne.class,true);
System.out.println("Iteration");
Iterator it = e.iterator();
while (it.hasNext()) {
Personne p = (Personne) it.next();
System.out.println(p);
System.out.println("SecuId: " + p.getSecuid());
int sid = p.getSecuid();
sid ++;
System.out.println("Modification du secuid avec "+sid);
p.setSecuid(sid);
System.out.println("Nouveau SecuId: " + p.getSecuid());
}
tx.commit();

Le lancement du programme de test Test4 donne la sortie suivante :

Ant run.test4
run.test4:
[java] Extent sur la class Personne
[java] Iteration
[java] Moussaud Benoit Date de naissance: 07/05/1973
[java] SecuId: 12345
[java] Modification du secuid avec 12346
[java] Nouveau SecuId: 12346

On a modifié le numéro de secu de Mr Moussaud ! On peut le vérifier en rappelant le programme Test3 :

Ant run.test3
run.test3:
[java] Extent sur la class Personne
[java] Iteration
[java] Moussaud Benoit Date de naissance: 07/05/1973
[java] SecuId: 12346
[java] OID   : 2

On a effectivement modifié l'instance ayant l'oid 2 avec un nouveau secuid. Si on jette un coup d'œil dans la base de données, on voit bien la modification :

mysql> select * from omd_personne;
+----------+--------+--------+--------+---------------------+
| NOM      | PRENOM | SECUID | LIDOID | DATEDENAISSANCE     |
+----------+--------+--------+--------+---------------------+
| Moussaud | Benoit |  12346 |      2 | 1973-05-07 00:00:00 |
+----------+--------+--------+--------+---------------------+

III-E - Suppression

Il manque une dernière étape à notre petite introduction : la suppression des instances : cette action s'effectue par la méthode deletePersistent() de la classe Persistent Manager.

suppression

tx.begin();

System.out.println("Extent sur la class Personne");
Extent e = pm.getExtent(Personne.class,true);
System.out.println("Iteration");
Iterator it = e.iterator();
while (it.hasNext()) {
Personne p = (Personne) it.next();
System.out.println(p);
System.out.println("SecuId: " + p.getSecuid());
System.out.println("Suppression de l'instance");
pm.deletePersistent(p);
}
tx.commit();

Le lancement du programme de test Test5 donne la sortie suivante :

Ant run.test5
run.test5:
[java] Extent sur la class Personne
[java] Iteration
[java] Moussaud Benoit Date de naissance: 07/05/1973
[java] SecuId: 12346
[java] Suppression de l'instance

On peut le vérifier en rappelant le programme Test3 :

Ant run.test3
run.test3:
[java] Extent sur la class Personne
[java] Iteration

Il n'y a aucune instance de la classe personne dans notre base. Si on regarde le contenu réel de la base de données, on voit :

mysql> select * from cud_personne;
Empty set (0.00 sec)

IV - FONCTIONS AVANCÉES

Dans cette partie nous allons faire persister un modèle un peu plus complexe et effectuer des requêtes JDOQL.
L'archive Complexe contient les sources, le script ANT, les fichiers de configuration et les programmes de test (Workspace Eclipse 3.0).

- Téléchargez Complexe Version Lido

- Téléchargez Complexe Version JPox

IV-A - Le modèle

Le modèle décrit une discothèque comprenant des artistes, des groupes, des albums, des pistes, genre… La pertinence du modèle n'est pas notre priorité ici, mais d'obtenir un modèle avec de l'héritage, des relations 1..1, des relations 1..n avec différentes collections. L'implémentation du modèle n'utilise que des classes standards Java ; aucune référence au package javax.jdo n'y est faite.

IV-B - metadata.jdo

Voyons maintenant le fichier metadata.jdo.

metadata.jdo

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jdo SYSTEM "jdo.dtd">
<jdo>
<package name="org.moussaud.data">
<class name="Artist" identity-type="datastore">
<!-- relation 1..n implementee sous forme d'une Map
associant une cles de classe String
et une valeur de classe Album -->
<field name="albums">
<map key-type="java.lang.String" embedded-key="true"
value-type="org.moussaud.data.Album"/>
</field>
<field name="name"/>
</class>
<class name="Genre" identity-type="datastore">
<field name="name"/>
</class>
<!-- definition de la classe Person heritant de la classe Artist -->
<class name="Person" identity-type="datastore"
persistence-capable-superclass="org.moussaud.data.Artist">
<field name="surname"/>
</class>
<class name="Track" identity-type="datastore">
<field name="tracknumber"/>
<field name="time"/>
<field name="name"/>
</class>
<!-- definition de la classe Group heritant de la classe Artist -->
<class name="Group" identity-type="datastore"
persistence-capable-superclass="org.moussaud.data.Artist">
<!-- relation 1..n sous forme de Set (HashSet) -->
<field name="members">
<collection element-type="org.moussaud.data.Person"/>
</field>
</class>
<class name="Album" identity-type="datastore">
<field name="artist"/>
<field name="date"/>
<field name="genre"/>
<field name="name"/>
<!-- relation 1..n implementee sous forme de List (arraylist)
contenant des instances de la classe Track -->
<field name="tracks">
<collection element-type="org.moussaud.data.Track"/>
</field>
</class>
</package>
</jdo>

La remarque générale sur ce fichier dit de mapping est qu'à aucun endroit nous ne faisons référence à une base relationnelle. Ce fichier est unique, que l'on persiste dans une base relationnelle, une base objet, un mainframe ou autre support de persistance (ex. : Fichier XML). La conséquence est que sa lecture est très simple et qu'il ne contient que des informations décrivant notre modèle objet qui ont été masquées par le langage Java. En particulier on y spécifie les collections (leur type) et le type de valeurs pouvant y être retrouvé.

Comme pour l'exemple Simple, le répertoire contient un fichier ANT.

Ant compile
Ant enhance
[enhance] LiDO Enhancer
[enhance] LiDO 3.1.1 build 654 (15/04/2005)
[enhance] (C) 2001-2004 Xcalia
[enhance] Utilisation de(s) fichier(s) "..\ComplexeLido\src\org\moussaud\data\package.jdo"
[enhance] Enhancement de la classe org.moussaud.data.Album
[enhance] Enhancement de la classe org.moussaud.data.Artist
[enhance] Enhancement de la classe org.moussaud.data.Person
[enhance] Enhancement de la classe org.moussaud.data.Group
[enhance] Enhancement de la classe org.moussaud.data.Genre
[enhance] Enhancement de la classe org.moussaud.data.Track
[enhance] Ecriture des classes enhancÚes
[enhance] Le traitement a pris 1 secondes

Ant define-schema
efine-schema:
define-schema] LiDO DefineSchema
define-schema] LiDO 3.1.1 build 654 (15/04/2005)
define-schema] (C) 2001-2004 Xcalia
define-schema] Instrospection partielle de la base. Veuillez patienter...
define-schema] Connecting to database jdbc:mysql://localhost/test
define-schema] Loading JDBC driver com.mysql.jdbc.Driver
define-schema] Disabling auto-commit
define-schema] Schéma défini.

Si on regarde avec mysql les tables créées, on a :

mysql> show tables;
+-------------------+
| Tables_in_test    |
+-------------------+
| lidoidmax         |
| lidoidtable       |
| omd_album         |
| omd_album_tracks  |
| omd_artist        |
| omd_artist_albums |
| omd_genre         |
| omd_group         |
| omd_group_albums  |
| omd_group_members |
| omd_person        |
| omd_person_albums |
| omd_track         |
+-------------------+
13 rows in set (0.00 sec)

Une table par classe et une table pour toutes les relations 1..n : omd_album_track, omd_artist_album, omd_group_album, cud_person_album (la configuration de base sépare la classe de base (Artist) des classes dérivées (Person et Group)) et omd_group_members. Ceci est une génération automatique, correcte pour une première approche. En effet, il est toujours possible d'intervenir sur le schéma de la base à l'aide d'un DBA et d'ajuster le mapping en conséquence.

Vous remarquerez la présence de deux tables : lidoidmax et lidoidtable. La table lidoidmax sert à Lido pour stocker la valeur du dernier OID affecté. La table lidoidtable contient la relation entre un oid et le type de classe associés. Elle sert à Lido dans le cadre d'une requête du type getObjectById() de récupérer la classe concernée puis de charger l'instance associée à cet OID. Cela lui évite de parcourir l'ensemble des tables du modèle.

IV-C - Création de données

Le programme 'test.moussaud.CreateData' permet de remplir la discothèque avec quelques artistes, albums… Pour le lancer, utilisez la commande :

ant run.createdata

Si on regarde la structure de ce programme, on voit qu'une fois passé le bloc d'initialisation du driver JDO, une série d'instanciations d'objets ponctuées par un bloc de trois lignes :

tx.begin();
pm.makePersistent(pf);
tx.commit();

Ces trois lignes déclarent une nouvelle transaction (begin), rendent l'objet pf de type Group persistant et clos la transaction (commit). Par rapport aux nombres d'instances créées, cela peut paraître bien peu. Eh bien non ! JDO inclut un mécanisme dit de « persistance transitive ». J'explique. Quand on applique la méthode makePersistent à une instance, le driver va analyser les dépendances de cet objet et va rendre aussi persistants (s'ils ont été déclarés comme tels) les objets appartenant au graphe d'objets formé par l'instance. Si l'objet n'est pas encore persistant, il le devient (avec attribution d'un oid), s'il l'est déjà, le driver va le repérer pour constituer un graphe d'objets persistants et cohérents (pas de duplication d'objet). C'est d'ailleurs ce qui fait avec l'instance rock qui réellement partagée et utilisée autant de fois que la relation existe.

IV-D - Requêtes

On va se servir de ce modèle un peu plus complexe pour voir ce que JDO apporte dans ce domaine. Le langage de requête de JDO est le JDOQL.

IV-D-1 - Vérification du nombre d'instances

Nous allons commencer par vérifier que l'on a bien le bon nombre d'instances de chaque classe dans notre source de données. Dans l'exemple simple, nous avons vu que comment récupérer l'ensemble des instances d'une classe avec l'interface Extent. Un autre moyen est d'utiliser l'interface Query. La fonction check() vérifie si on a le nombre d'instances de la classe passée en paramètre. Sinon elle envoie une erreur.

vérification

private static void check(PersistenceManager persistenceManager,Class c,int size) {
Query query = persistenceManager.newQuery(c);
Collection result = (Collection) query.execute();
if (result.size() != size)
throw new Error("Les nombres d'instances de " + c
+ " est invalide: attendu(" + size + ")
trouvé("+ result.size() + ")");
System.out.println("Nombre d'instances de " + c + " = " + size);
}

On demande au PersistenceManager de créer une Query sur la classe passée en paramètre et on exécute celle-ci. La spécification JDO dit que cette méthode (execute()) devait renvoyer un objet et pas directement une collection. À mon sens ceci n'est pas très pratique, car cela revient à transtyper l'instance retournée en Collection. À partir de là, on a une collection Java classique et l'on peut lui demander sa taille. Si la taille est égale à celle attendue, c'est bon sinon message d'erreur.

Ant run.checkdata
run.checkdata:
[java] 3 albums de Pink Floyd, 1 de Television
[java] et le best of d'Enio Morricone
[java] Nombre d'instances de class org.moussaud.data.Album = 5

[java] Pink Floy et Television
[java] Nombre d'instances de class org.moussaud.data.Group = 2

[java] Enion Moricone, les 5 membres de PF
[java] Nombre d'instances de class org.moussaud.data.Person = 6

[java] Groupe + Person
[java] Nombre d'instances de class org.moussaud.data.Artist = 8

[java] rock et blues
[java] Nombre d'instances de class org.moussaud.data.Genre = 2

[java] Nombre d'instances de class org.moussaud.data.Track = 38

IV-D-2 - Les Tris

Par défaut, si rien n'est précisé les instances sont renvoyées dans l'ordre de la base de données, le plus souvent classées par OID. (C'est-à-dire dans l'ordre où les instances ont été insérées.) Pour cela, il suffit d'indiquer à l'objet Query dans quel ordre on désire les instances : Quert.setOrdering(String). Le paramètre est une chaîne de caractères indiquant le nom de l'attribut suivi du mot clé ascending ou descending.

tris

Query qt = pm.newQuery(Album.class);
qt.setOrdering("name ascending");
Collection cqt = (Collection) qt.execute();
Iterator itqt = cqt.iterator();
while (itqt.hasNext()) {
Album a = (Album) itqt.next();
System.out.println(" "+a.getName());
}
qt.closeAll();

exemple

ant run.sortdata
run.sortdata:
[java] Requete: tous les albums
[java]  The Wall
[java]  Dark Side Of The Moon
[java]  Wish you Were Here
[java]  Marquee Moon
[java]  Compilation

[java] Requete: tous les albums triés par nom
[java]  Compilation
[java]  Dark Side Of The Moon
[java]  Marquee Moon
[java]  The Wall
[java]  Wish you Were Here

IV-D-3 - Requêtes avec paramètres

Il est possible, comme pour les requêtes JDBC, de paramétrer la requête. Ceci se fait par la méthode Query.setFilter() et Query.declareParameters(). La méthode setFilter() prend en paramètre une expression conditionnant les instances correspondant à la classe passée en paramètre de la requête. La méthode declareParameters() indique au driver le nom et le type du ou des paramètres. L'exemple suivant recherche tous les albums 'rock'.

avec paramètres

System.out.println("Recherches des Albums de rock");
Query qarock = pm.newQuery(Album.class);
qarock.setFilter("genre.name == style");
qarock.declareParameters("String style");
Collection result = (Collection) qarock.execute("rock");
for (Iterator iter = result.iterator(); iter.hasNext();) {
Album a = (Album) iter.next();
System.out.println("--> " + a);
}
qarock.closeAll();

exemple

ant run.queryparameter
run.queryparameter:
[java] Recherches des Albums de rock
[java] --> The Wall - Pink Floyd (1979) rock
[java] --> Dark Side Of The Moon - Pink Floyd (1973) rock
[java] --> Wish you Were Here - Pink Floyd (1975) rock
[java] --> Marquee Moon - Television (1978) rock

IV-D-4 - Requêtes avec variables

Pour effectuer certaines requêtes, il est nécessaire de passer par une variable intermédiaire dans l'expression du filtre. Ceci est surtout vrai quand il faut appliquer un filtre sur une collection (relation 1..n). La variable nécessite d'être importée, c'est-à-dire définie par une clause ressemblant à un import par la méthode Query.declareImports(). Ensuite il faut instancier la variable par la méthode Query.déclareVariables().

recherche les albums avec une piste commençant par M

String letter = "M";
System.out.println("Recherches des Albums avec "
+ "une piste commençant par M");
Query qal = pm.newQuery(Album.class);
qal.declareParameters("String letter");
qal.declareVariables("Track track");
qal.declareImports("import org.moussaud.data.Track");
qal.setFilter("tracks.contains(track) "
+ " && track.name.startsWith(letter)");
Collection result = (Collection) qal.execute(letter);
for (Iterator iter = result.iterator(); iter.hasNext();) {
Album a = (Album) iter.next();
System.out.println("--> " + a);
System.out.println(a.displayTracks());
}
qal.closeAll();

exemple

ant run.queryvariable
run.queryvariable:
[java] Recherches des Albums avec une piste commençant par M
[java] --> The Wall - Pink Floyd (1979) rock
[java]   #1-In the flesh? 03:17
[java]   #2-The thin ice 02:28
[java]   #3-Another brick in the wall (part i) 03:41
[java]   #4-The happiest days of our lives 01:20
[java]   #5-Another brick in the wall (part ii) 03:56
[java]   #6-Mother 05:32
[java]   #7-Goodbye blue sky 02:48
[java]   #8-Empty spaces 05:36
[java]   #9-Young lust 02:03
[java]   #10-One of my turns 01:33
[java]   #11-Don't leave me now 04:22
[java]   #12-Another brick in the wall (part iii) 01:17
[java]   #13-Goodbye cruel world 01:05

[java] --> Dark Side Of The Moon - Pink Floyd (1973) rock
[java]   #1-Speak to me - breathe 04:00
[java]   #2-On the run 03:32
[java]   #3-Time/The great gig in the sky 07:06
[java]   #4-Money 06:32
[java]   #5-Us and them 07:40
[java]   #6-Any colour you like 03:25
[java]   #7-Brain damage 03:50
[java]   #8-Eclipse 02:04

[java] --> Marquee Moon - Television (1978) rock
[java]   #1-See no evil 03:53
[java]   #2-Venus 03:51
[java]   #3-Friction 04:44
[java]   #4-Marquee Moon 10:40
[java]   #5-Elevation 05:07
[java]   #6-Guiding Light 05:35
[java]   #7-Prove it 05:02
[java]   #8-Torn Curtain 06:56

V - LE FUTUR

V-A - Fonctions avancées

Ce court tutoriel vous a brossé une première vision de JDO et de ses implémentations : Lido et JPox. Il faut garder à l'esprit que chaque fournisseur de Driver propose des extensions propriétaires (qui sont en cours de spécification dans JDO 2.0). En partie pour tout ce qui concerne le type et le schéma de la base (contraintes de taille des champs, clés étrangères inversées pour les collections…) et aussi pour tout ce qui concerne les transactions en mode optimiste et pessimiste des différents moteurs de base de données. Les exemples montrent bien que choisir une implémentation JDO n'est pas un choix définitif. Si lors du développement d'un projet JDO, le driver retenu présentait des manques (performances, utilisation, licences…), il est assez aisé d'en changer ! Un peu comme les serveurs d'applications J2EE, toutes les spécificités sont dans les descripteurs XML. Donc peu d'impact sur le code applicatif.

Pour en savoir plus sur l'intégration d'un driver JDO et Tomcat (Conteneur WEB/J2EE), reportez-vous à l'article Utilisation de JDO dans une application web (JPox/Tomcat)

V-B - JDO 2.0

La spécification JDO 2.0 a été approuvée en mars 2005. L'une des fonctions attendues est attach()/detach() permettant de dissocier une instance du persistent manager afin d'être par exemple envoyé à une JSP pour y être affichée. Cela permettra de se passer du célèbre pattern DAO/VO (Data Access Object / Value Object). Un rapprochement vers EJB3 est en cours… Une autre avancée concerne la spécification du mapping vers les bases de données relationnelles.

V-C - Liens utiles

- Java Data Object (Sun)

- Description détaillée de l'API JDO

- JDO Central

- Un article très complet sur JDOQL

- Le site de Xcalia

- Le site de JPox

V-D - Crédits et Licence

Ce tutoriel est écrit par Benoit Moussaud, Consultant Sénior en Architecture J2EE chez Xebia
Pour un contact ou des précisions, n'hésitez pas à m'écrire : bmoussaud@xebia.fr
Copyrights Benoit Moussaud 2005