1. QU'EST-CE QUE JDO ?

1.1. 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 relationnelles, 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étiers complètement indépendant 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 ( !!) sont pris en charge par JDO.
  • Isole les compétences métiers 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 à moyen. 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 multi-tiers

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) ou JDO apporte un vrai gain. Les drivers génèrent du SQL parfois mieux que les dévoppeurs (souvent débutant) eux même. Dans les applications plus critiques ou moins standard, les drivers JDO permettent soit de prendre la main (récupérer la Datasource et effectuer la requete à 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 technologies, il faut employer JDO à bon escient dans les cas prévus pour.

1.2. Architecture

Image non disponible

JDO se place entre les objets métiers et les Api d'accès aux sources de données : JDBC pour les bases de données relationnelle, 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.

1.3. 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 rentre persistant et de pouvoir persister l'ensemble des classes Java.
Cette étape se situe juste après la compilation, comme le montre ce schéma :

Image non disponible

L'Enhancement est complètement définit 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 quel source de données
  • Permet toujours de débuggage (JPDA)

Et assure une persistence complètement transparente.

2. INSTALLATION DES COMPOSANTS

Dans notre tutorial 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.

2.1. 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 tutorial 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.
Editer ou créer le ficher c:/windows/my.ini avec les informations suivantes

mysql.ini
Sélectionnez
[mysqld]
basedir=C:/tutoriel/mysql
datadir=C:/tutoriel/mysql/data
language=C:/tutoriel/mysql/share/french
port=3306

Aller dans le répertoire C:/tutoriel/mysql/bin et lancer mysqld-max-nt install pour installer le moteur en tant que service NT. Puis lancer 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.
Extraire le fichier .zip et copier le fichier mysql-connector-java-3.0.14-production-bin.jar sous c:/Tutoriel/myslq/jdbc (répertoire à créer).

2.2. Java

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

 
Sélectionnez
Ex : JAVA_HOME= D:\DevTools\j2sdk1.4.2_04

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

 
Sélectionnez
PATH=%JAVA_HOME%\bin;%PATH%

2.3. Ant

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

  • Télécharger ANT en version 1.6.x sur le site de jakarta
  • Extraire l'archive sous C:\tutoriel\apache-ant-1.6.2
  • Déclarer une variable d'environnement ANT_HOME pointant sur le répertoire ANT: ANT_HOME = C:\tutoriel\apache-ant-1.6.2
  • Ajouter au path le chemin %ANT_HOME%\bin: PATH=%ANT_HOME%\bin;%PATH%
  • Ouvrer une console et vérifier que ANT est bien trouvé:
 
Sélectionnez
C:\ ant -version
Apache Ant version 1.6.2 compiled on July 16 2004

2.4. Lido XD

Image non disponible 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 à 30mn pour les bases de données commerciales ou/et l'utilisation avec un serveur d'application J2EE. Toutes les fonctionnalitées de LiDO XD sont incluses dans la licence gratuite de développement, valable 6 mois et renouvelable par téléchargement, cependant celles permettant un déploiement commercial sont limitées à 30mn. 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 environnement 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. Positionner 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%

Image personnelleSi vous avez encore des problèmes lors du lancement de la commande : ant enhance, lancer le script suivant:
 
Sélectionnez

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

2.5. JPox

Image non disponible JPox est une implémentation sous license Open Source Apache 2 de la spécification JDO. Pour le moment, cette équipe se concentre à une persitance vers les bases de données relationnelles tandis que Lido adresse un plus large éventaille de source (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 technologies JSP/Servlet.

Les différents exemples pour JPox contiendrons les drivers jpox nécessaire à 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.

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

3. 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).

3.1. Le modèle

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

Personne.java
Sélectionnez

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
Sélectionnez

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 lancer

 
Sélectionnez

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!

3.2. 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
Sélectionnez

<?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>

3.3. 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
Sélectionnez

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));
}

A 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
Sélectionnez

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

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

 
Sélectionnez

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

3.4. 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
Sélectionnez


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 par donne la sortie suivante:

 
Sélectionnez

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

 
Sélectionnez

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.

 
Sélectionnez

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

3.5. 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
Sélectionnez

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 par donne la sortie suivante:

 
Sélectionnez

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.

 
Sélectionnez

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

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

 
Sélectionnez

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

4. FONCTIONS AVANCEES

Dans cette partie nous allons faire persister un modèle un 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).

4.1. 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 standard java ; aucune référence au package javax.jdo n'y est faite.

Image non disponible

4.2. metadata.jdo

Voyons maintenant le fichier metadata.jdo.

metadata.jdo
Sélectionnez

<?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 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é 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.

 
Sélectionnez

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 :

 
Sélectionnez

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 classes 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, correct 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 le présence de 2 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 evite de parcourir l'ensemble des tables du modèle.

4.3. Création de données

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

 
Sélectionnez

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'instanciation d'objets ponctué par un bloc de 3 lignes :

 
Sélectionnez

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

Ces 3 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 inclus 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 persistant (s'ils ont été déclaré comme tel) les objets appartenants au graphe d'objet 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.

4.4. 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.

4.4.1. Vérification du nombre d'instance

Nous allons commencer par vérifier que l'on a bien le bon nombre d'instance 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'instance de la classe passée en paramètre. Sinon elle envoie une erreur.

vérification
Sélectionnez

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'instance de " + c
				+ " est invalide: attendu(" + size + ")
    				trouvé("+ result.size() + ")");
		System.out.println("Nombre d'instance 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. A mon sens ceci n'est pas très pratique car cela revient à transtyper l'instance retournée en Collection. A 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.

 
Sélectionnez

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'instance de class org.moussaud.data.Album = 5

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

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

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

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

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

4.4.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 ou 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ère indiquant le nom de l'attribut suivi du mot clé ascending ou descending.

tris
Sélectionnez

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
Sélectionnez

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

4.4.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 de ou des paramètres. L'exemple suivant recherche tous les albums 'rock'.

avec paramètres
Sélectionnez

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
Sélectionnez
	
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

4.4.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().

recherches les albums avec une piste commençant par M
Sélectionnez

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
Sélectionnez

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

5. LE FUTUR

5.1. Fonctions avancées

Ce court tutorial 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ée 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, licenses,...) 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)

5.2. 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.

5.3. Liens Utiles

5.4. Crédits et License

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