Les Java Data Objects (version 1.0.1)

Romain Guy (Gfx)

Avec le développement d'importantes applications Web, la notion de persistance des objets a pris une importance considérable. Si ces termes nous font invariablement penser à la sérialisation, sachez que certaines technologies vont beaucoup plus loin. ♪

- Titre : Les Java Data Objects (version 1.0.1)

- Auteur : Romain Guy (Gfx)

- Parution : 10 juillet 2006

- Licence : Cette création est mise à disposition sous un contrat Creative Commons (Paternité - Partage des Conditions Initiales à l'Identique).

Sommaire

- Introduction

- I. Mécanismes sous-jacents

- II. Persistons dans nos démarches

- III. Premier exemple

- IV. Requêtes

- V. Gestion de l'héritage

- VI. Gestion des collections

- VII. Conclusion

- VIII. Screenshots

- IX. Bibliographie

- X. Liens

Introduction

Note de la rédaction : ce tutoriel avait été publié à l'origine dans le défunt magazine Login.

Les Java Data Objects, ou JDO, désignent une spécification destinée à la persistance des données en Java. Parmi l'ensemble de ses caractéristiques, nous pouvons en isoler certaines, très importantes. En premier lieu, JDO implique une notion de transparence. En dehors de certaines instructions explicites (tel l'appel de méthode makePersitent) ou des début et fin de transaction, la gestion de la persistance des données ne doit pas paraître aux yeux du développeur. Nous pouvons comparer cet acte implicite à la gestion de la mémoire par le ramasse-miettes (GC) de la machine virtuelle.
Vient ensuite l'universalité du support. Nous choisirons dynamiquement le type de support utilisé comme support de stockage, que cela soit des bases de données ou des fichiers. En outre, le même objet JDO doit fonctionner de manière similaire dans tout autre environnement Java, sans intervention particulière.
Nous pouvons également compter sur l'élimination des syntaxes spécialisées. Ainsi, des requêtes SQL disparaîtront pour laisser place au seul langage Java. L'approche pour le programmeur se révèle donc particulièrement consistante, quelle que soit la source de données sélectionnée. Une syntaxe particulière, le JDO Query Language (ou JDOQL) unifie le langage des requêtes et Java.

I - Mécanismes sous-jacents

Afin de rendre un objet persistant, nous devons le décrire par l'entremise d'une syntaxe XML abstraite. Celle-ci se trouve employée dans un fichier de métadonnées qui, dans le monde JDO, s'intitule metadata.jdo. Ce fichier explique précisément de quelle manière aura lieu la persistance : quels sont les champs à traiter, détermination des clés, noms des champs, et ainsi de suite.
À l'instar de RMI, l'utilisation de JDO nécessite une opération d'enrichissement (ou enhancement) du bytecode. Toutefois, en lieu et place des « stubs » et « skeletons » nous modifions ici directement le fichier .class de la classe Java dont les objets seront persistants.
Le second mécanisme vital concerne la définition de schéma. Il s'agit ici de rédiger un fichier de propriétés caractérisant le type de support employé pour la gestion de la persistance, le pilote, l'URL de connexion, les informations d'identification, et ainsi de suite. Dans le cas d'un stockage au sein d'une base de données, nous pourrons nous reposer sur une base existante ou en générer une nouvelle à partir du fichier de métadonnées.
Cette technologie rentre dans le vaste domaine du mapping d'objets, dont le représentant le plus en vogue est le mapping d'objets relationnel. En d'autres termes, si elle a été conçue pour opérer indépendamment du type de support, les bases de donnes relationnelles représentent le choix le plus courant, pour le moment. L'emploi de JDO prend toute sa valeur dans le cadre de la persistance automatique d'objets. Par exemple, sur un serveur d'application J2EE, les Enterprise Java Beans Container-Managed Persistence (EJB CMP) pourront bénéficier de toute la puissance de cette technologie. La norme J2EE Connector Architecture (JCA) instaure à cet effet une collaboration pour permettre à JDO de se substituer au moteur interne de persistance du serveur.

II - Persistons dans nos démarches

Les Java Data Objects sont soumis à une spécification de Sun Microsystems, au moment de la rédaction de cet article en version 1.0.1. Outre l'implémentation de référence, JDO RI du même auteur, nous pouvons en trouver de nombreuses autres. Deux compagnies en particulier proposent leurs outils. Nous avons ainsi LiDO de LIBeLIS, et Kodo JDO de Solarmetrics. La première offre notamment un serveur d'application Java Open Source intitulé Orcas, qui nous permettra de nous essayer facilement à LiDO. Pour rester dans le monde de l'Open Source, sachez que TJDO et OJB offrent des solutions alternatives intéressantes. Bien que parfaitement conformes à la spécification de Sun Microsystems, ces dernières se révèlent malheureusement moins abouties que celles précédemment citées. Dans cet article, nous ferons appel à LiDO en version communauté et au bien connu gestionnaire base de données MySQL.
Nous allons découvrir comment appliquer les traitements JDO adéquats à de simples classes Java prenant la forme de Java Beans, c'est-à-dire offrant une collection d'accesseurs et de mutateurs. Par ailleurs, nous utiliserons le terme générique de source de données et laisserons de côté l'appellation base de données. Bien que nos exemples reposent sur MySQL, nous pourrions envisager des fichiers XML, un système de gestion de base de données orienté objet…
Au commencement, aucun objet du système d'informations ne peut candidater au processus de persistance, même si nous les avons décrits dans le fichier de métadonnées. Seul un appel à la méthode makePersistent() activera le système de persistance. Cette invocation peut d'ailleurs se révéler inutile si nous rendons persistant un objet contenant une référence vers un autre, toujours « temporaire ». La persistance de ce dernier sera appliquée grâce au phénomène de « persistance par liaison ». Enfin, sachez que la spécification JDO prévoit la gestion des transactions. Nous pourrons donc effectuer des opérations de lecture, d'écriture, et de modification en accès concurrent suivant deux algorithmes, l'un dit optimiste et l'autre dit pessimiste.

III - Premier exemple

Avant de nous intéresser aux cas complexes de l'agrégation, de la composition, de l'héritage et des collections, nous allons apprendre à faire persister une classe simple, nantie de quelques accesseurs et mutateurs. Imaginons que notre système nécessite de conserver des informations sur des personnes, représentées par la classe Person définie dans le listing 1.
La première tâche du développeur consiste à décrire les objets persistants dans le fichier metadata.jdo. Les quelques lignes suivantes présentent son contenu dans le cas de notre classe company.Person :

<jdo>
<package name="company">
<class name="Person" />
</package>
</jdo>

listing 1

package company;

public class Person {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

Il s'agit ici d'une description minimale. Dans ce cas précis, JDO considérera la classe comme globalement persistante. Tous ses champs le seront donc. Nous retrouvons ici l'une des caractéristiques évoquées précédemment, la transparence du procédé. Il va de soi que seules les classes désignées dans metadata.jdo pourront bénéficier de cette technologie.
Après compilation du code source Java, le programmeur doit procéder à l'étape d'enrichissement. Dans notre cas, nous devrons exécuter la commande suivante dans la console système :

java com.libelis.lido.Enhance -metadata metadata.jdo

Nous adaptons ainsi notre bytecode à l'API JDO.
L'étape suivante consiste à définir le schéma d'accès à la source de données. Pour ce faire, une nouvelle commande doit être saisie dans la console système !

java com.libelis.lido.ds.jdbc.DefineSchema -properties lido_mysql.properties -c

Le fichier auquel nous faisons ici appel se trouve dans les sources en annexe et contient toutes les propriétés nécessaires pour désigner la base de données Test du serveur MySQL local comme source de données. Il vous faudra impérativement posséder le driver MySQL pour JDBC.
En modifiant le fichier lido_mysql.properties, puis en exécutant de nouveau cette commande, vous changerez rapidement et efficacement le type de source de données. Encore une fois, l'opération se révèle transparente finalement pour le développeur. L'option -c utilisée dans notre exemple demande la création des tables dans la base de données. Évidemment, si celles-ci existent déjà, son emploi les détruira. Sachez enfin que malgré l'utilisation d'un fichier de propriétés externe, nous pourrions tout à fait déclarer chacune d'entre elles au cœur même de notre programme, dans le code source. Nous perdrions toutefois une certaine souplesse.
Au cours de l'exécution, nous obtenons une trace des différentes requêtes SQL effectuées par LiDO. Nous découvrons notamment la création de la table correspondant à notre classe :

CREATE TABLE c_Person ( LIDOID BIGINT NOT NULL , name VARCHAR(255) NULL , age INT NULL  )

Le premier champ, un type entier intitulé LIDOID, joue le rôle d'une clé implicite, rajoutée par l'outil de définition du schéma. Les champs restants correspondent pour leur part aux propriétés de la classe. Nous remarquerons enfin que la chaîne « c_ » préfixe le nom de la table. Il s'agit de la première lettre du paquetage, afin d'éviter tout conflit de nommage au sein de la source de données.
Nous pouvons à présent rédiger un programme de test qui nous servira d'une part à remplir la source de données et d'autre part à vérifier la possibilité de récupération des données. La partie la plus compliquée consiste à préparer la connexion vers la source de données. Le listing 2 présente les quelques lignes de code, spécifiques à notre exemple, que nous utiliserons. Ce code source crée une instance de la fabrique PersistenceManagerFactory, à laquelle nous passons certaines propriétés permettant d'identifier la source et de s'y connecter avec succès. Cet exemple utilise non seulement une collection de propriétés, mais également des mutateurs de la classe. Nous aurions pu utiliser uniquement des propriétés et, notamment, toutes les charger depuis le fichier lido_mysql.properties déjà étudié. Cette instance fournit un gestionnaire de données adapté à la source considérée, représenté par une instance de la classe PersistentManager. Notre programme utilisera ce dernier pour activer la persistance des données. Ce même gestionnaire nous donne accès à la transaction courante, fonctionnant sur le même principe que les transactions SQL. L'extrait suivant expose la marche à suivre pour dupliquer un objet Person dans la source de données :

tx.begin();
Person person = new Person("Paul", 24);
pm.makePersistent(person);
tx.commit();

listing 2

Properties prop = new Properties();
prop.put("javax.jdo.PersistenceManagerFactoryClass", "com.libelis.lido.PersistenceManagerFactory");

PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(prop);
pmf.setConnectionDriverName("org.gjt.mm.mysql.Driver");
pmf.setConnectionURL("jdbc:mysql://localhsot/test");
pmf.setConnectionUserName("root");

PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();

La simplicité de JDO ne peut que séduire. Bien sûr, le stockage à proprement parler ne se voit réalisé qu'après invocation de la méthode commit() de l'objet Transaction. Une fois un objet présent dans la source de donnée, nous pouvons le récupérer grâce à son identificateur. Une manière simple de le faire consiste à obtenir ce dernier juste après l'appel de makePersistent() :

Obejct objID = pm.getObjectId(person);
tx.begin();
person = (Person) pm.getObjectById(paulId, true);
String name = person.getName();
System.out.println("Nom de Person = " + name);
tx.commit();

L'obtention d'un objet persisté a lieu par l'entremise de la méthode getObjectById(). Son second paramètre, un booléen, permet d'effectuer une validation de l'objet. En effet, JDO emploie un cache, et nous ne sommes pas assurés de la validité des objets de ce cache par rapport au contenu de la source de donnée. En mettant ce paramètre à vrai, nous obligeons JDO à comparer son cache à la source de données et à le mettre à jour si besoin est. Les différentes subtilités inhérentes à son utilisation, dans des cas particuliers, sont clairement énumérées dans la documentation de l'API JDO.

IV - Requêtes

Dans la pratique, l'obtention d'un objet à l'aide de son identificateur ne possède que peu d'intérêt. Fort heureusement, la spécification de Sun Microsystems prévoit la gestion de requêtes, similaires au SQL. Dans l'exemple proposé par le listing 3, nous récupérons les données stockées dans la source par l'entremise d'une requête JDO.
La création de la requête nécessite la spécification de la classe concernée par la recherche, c'est pourquoi nous passons en paramètre à newQuery() l'attribut class de Person. Nous pouvons ensuite appliquer un filtre pour limiter les résultats, présentés sous forme d'une Collection Java. La réalisation d'une requête dont le filtre reste vide équivaut à demander toutes les instances d'une classe.
L'exemple présenté repose sur la syntaxe JDOQL, qui n'est autre que du Java. Nous effectuons ici une requête récupérant toutes les personnes dont le nom commence par la lettre P. L'intérêt évident de ce langage de requête réside dans sa formidable homogénéité avec son langage d'encapsulation, Java. JDOQL apporte toutefois certaines nouveautés, comme la comparaison de dates ou de chaînes par l'intermédiaire des opérateurs classiques d'infériorité, supériorité et égalité.
Les requêtes JDOQL supportent également les paramètres. Ainsi, le code suivant installe un paramètre nommé « name » auquel nous faisons référence dans la requête. Lors de l'appel de la méthode execute() nous devons fournir la valeur des différents paramètres :

Gestion de l'agrégation et de la composition

query.declareParameters("String name");
String filter = "this.name == name";
Collection persons = (Collection) query.execute("Paul");

listing 3

System.out.println("Récupération de l'objet \"Paul\" par Query");
Query query = pm.newQuery(Person.class);
String filter = "name.startsWith(\"P\")";
query.setFilter(filter);
Collection persons = (Collection) query.execute();
Iterator it = persons.iterator();
while (it.hasNext()) {
person = (Person) it.next();
String name = person.getName();
System.out.println("Nom de Person = " + name);
}

L'exemple étudié ne reflète que pauvrement la réalité des systèmes d'information. Nous allons donc l'étoffer en introduisant les notions d'agréation et de composition. Pour cette dernière, nous modifions la classe Person et lui ajoutons deux membres permettant de spécifier l'adresse d'une personne et ses qualifications professionnelles. Puisqu'une adresse appartient à une seule personne, nous allons encapsuler la classe Address, par composition, dans Person. Plutôt que d'étendre les champs de cette dernière, nous définirons l'encapsulation comme un nouveau champ, qui accueillera une version sérialisée de l'objet. Le listing 4 décrit la classe Address. Vous constaterez que celle-ci implémente l'interface java.io.Serializable, pour permettre à la source de données de la stocker, dans notre cas, au sein d'un champ BLOB de la table c_Person. La mise en place de la composition nécessite de modifier sensiblement le fichier metadata.jdo :

<class name="Person">
<field name="address" embedded="true"/>
</class>

listing 4

package company;

public class Address implements java.io.Serializable {
private String coordinates;

public Address(String coordinates) {
this.coordinates = coordinates;
}

public String getCoordinates() {
return coordinates;
}

public void setCoordinates(String coordinates) {
this.coordinates = coordinates;
}

public String toString() {
return "[Address] coordinates = " + coordinates;
}
}

L'élément <field /> autorise l'activation de la persistance d'une instance de la classe Address, le membre d'instance « address » de Person. Gardez bien à l'esprit que la persistance n'est effective que dans ce cas précis, un objet Address seul ne pourra pas bénéficier du procédé de stockage.
Nous introduisons l'agrégation grâce à une référence vers la classe Skills dans Person. Contrairement à Address, nous ne ferons pas appel à la sérialisation et opterons pour une déclaration de persistance normale dans le cadre de JDO. Le listing 5 présente la classe Skills dont la définition de persistance au sein de metadata.jdo prend la forme suivante :

<class name="Skills" />

listing 5

package company;

public class Skills {
private String diploma;

public Skills(String diploma) {
this.diploma = diploma;
}

public String getDiploma() {
return diploma;
}

public void setDiploma(String diploma) {
this.diploma = diploma;
}
}

En examinant les traces émises par LiDO au cours de l'exécution du procédé de définition des schémas, nous pouvons observer les lignes suivantes :

CREATE TABLE c_Person ( LIDOID BIGINT NOT NULL , address LONGBLOB NULL , name VARCHAR(255) NULL , age INT NULL , level BIGINT NULL  )
CREATE TABLE c_Skills ( LIDOID BIGINT NOT NULL , diploma VARCHAR(255) NULL  )

La différence entre une composition (réalisée par l'élément <field /> dont l'attribut embedded vaut vrai) et une agrégation apparaît clairement : la classe Skills possède sa propre table, au contraire de Address. Pour récupérer toutes les informations relatives à une personne, nous devons réaliser une jointure entre les tables c_Person et c_Skills, ce que permet le champ level de la première, correspondant à la clé LIDOID de la seconde.
Notre nouveau programme d'essai reprend la structure du premier. Néanmoins, nous devons à présent préciser les attributs adresse et qualification de notre donnée d'exemple.

person.setAddress(new Address("Adresse de Paul"));
person.setLevel(new Skills("Technicien"));

Après récupération d'une instance de Person depuis la source de données, nous pouvons très facilement récupérer, toujours de manière totalement transparente, les nouveaux attributs :

Address address = person.getAddress();
Skills level = person.getLevel();

V - Gestion de l'héritage

L'une des difficultés que nous pouvons nous attendre à rencontrer concerne la persistance de chaînes d'héritage. Nous allons spécialiser notre exemple en créant une classe Employee, héritant de Person. Cette nouvelle entité permet d'attribuer un salaire à notre employé. Le listing 6 offre le code source complet. JDO doit maintenant savoir que notre nouvelle classe hérite de Person. Pour ce faire, nous modifions une fois de plus le fichier de métadonnées et ajoutons la ligne suivante :

<class name="Employee" persistence-capable-superclass="company.Person" />

listing 6

package company;

public class Employee extends Person {
private float salaire;

public Employee(String name, int age, float salaire) {
super(name, age);
this.salaire = salaire;
}

public float getSalaire() {
return salaire;
}

public void setSalaire(float salaire) {
this.salaire = salaire;
}
}

La gestion de l'héritage consiste à l'ajout d'un attribut persistence-capable-superclass dans l'élément class. La valeur de celui-ci doit représenter le nom complet (avec le paquetage donc) de la classe parente qui doit impérativement avoir été définie persistante pour JDO. Une fois de plus, nous pouvons examiner les requêtes SQL générées par LiDO pour bien comprendre le procédé de stockage sous-jacent :

CREATE TABLE c_Employee ( LIDOID BIGINT NOT NULL , address LONGBLOB NULL , name VARCHAR(255) NULL ,
age INT NULL , level BIGINT NULL , salaire FLOAT NULL  )

Nous remarquons que la table c_Employee reprend tous les champs de c_Person et y ajoute les siens propres, ici le champ salaire. Pour les besoins de nos essais, il nous suffit de remplacer l'instance de Person par une instance d'Employee, à partir de laquelle nous vérifierons que le salaire a été conservé dans la source de données.

Employee person = new Employee("Paul", 24, (float) 40000.00);
// ...
float salaire = person.getSalaire();

VI - Gestion des collections

Toujours dans le but d'adapter nos connaissances à des situations réelles, nous allons à présent aborder la gestion des collections. En considérant qu'un employé appartient à une entreprise, il va de soi que la classe Company accueillera une Collection d'employés, représentés par des instances de Employee. Nous trouverons dans le listing 7 la définition complète de notre nouvelle classe. L'interface publique de cette dernière propose le strict minimum pour la gestion des employés. Nous pouvons ainsi en ajouter à l'aide de addEmployee() et récupérer l'ensemble par l'entremise de getEmployee().
Comme à l'accoutumée, nous devons appliquer quelques modifications au fichier de métadonnées, à savoir l'ajout d'un nouvel élément :

<class name="Company">
<field name="employees">
<extension vendor-name="libelis" key="collection-type" value="java.util.List"/>
</field>
</class>

listing 7

public class Company {
String name;
private List employees = new ArrayList();

public Company(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List getEmployees() {
return employees;
}

public void addEmployee(Employee employee) {
employees.add(employee);
}
}

Nous observons ici ce qui distingue un champ de Collection d'un autre. Nous devons impérativement qualifier celui-ci en précisant le type sélectionné. Prenez garde, car, contrairement à tout ce que nous avons vu jusqu'à présent, « collection-type » désigne une extension propriétaire au standard JDO. Seule la bibliothèque LiDO autorise son emploi. Nous pouvons enfin examiner les requêtes SQL générées :

CREATE TABLE c_Company ( LIDOID BIGINT NOT NULL , name VARCHAR(255) NULL  )
CREATE TABLE c_Company_employees ( LIDOFK BIGINT NOT NULL , LIDOVALUE BIGINT NULL , LIDOPOS DOUBLE NULL  )

À n'en pas douter, l'ajout d'une collection complique les choses. La table c_Company pour sa part ne recèle rien de spécial. Toutefois, c_Company_employee ne ressemble en rien à ce que nous avons étudié jusqu'à présent, même le familier LIDOID manquant à l'appel. En réalité, cette table fait office de table de liaison. La clé, intitulée LIDOFK pour LIDO Foreign Key, désigne une clé étrangère liée à la clé primaire LIDOID de c_Company. Les deux autres champs, LIDOPOS et LIDOVALUE, désignent respectivement la position d'un objet dans la collection et la valeur associée à cette position. Cette fois, de plus amples modifications apparaissent dans le programme de test. Nous créons au total 3 employés que nous affectons à une compagnie. Pour récupérer la liste de nos employés, nous conserverons trace de l'identificateur de l'instance de Company.

Company company = new Company("boite");
pm.makePersistent(company);
Object companyId = pm.getObjectId(company);
Employee person = new Employee("Paul", 24, (float) 40000.00);
company.addEmployee(person);
person = new Employee("Jean", 37, (float) 45000.00);
company.addEmployee(person);
person = new Employee("Martine", 32, (float) 48000.00);
company.addEmployee(person);

Le listing 8 présente une méthode pour lister, à partir de l'identificateur de l'entreprise tous ses employés. Encore une fois, nous pouvons apprécier les facilités offertes par les Java Data Objects. Nous allons pour terminer effectuer une recherche plus complexe qui recherche un employé précis au sein d'une entreprise donnée. Vous pourrez vous reporter au listing 9 pour lire le code source correspondant.
Outre l'habituelle syntaxe Java pour mener la recherche, nous pouvons remarquer la déclaration de deux paramètres de requêtes, compName et empName. Ceux-ci permettent de spécifier l'entreprise et le nom de l'employé. En sus, nous déclarons une variable de requête par le biais de l'instruction query.declareVariables(). Lors de la recherche, LiDO va réaliser une jointure entre les tables c_Company et c_Company_employee pour automatiquement assigner à notre variable la valeur de chaque employé de la collection. Nous demandons explicitement cette liaison lors de l'invocation de l'instruction de filtre contains().

listing 8

company = (Company) pm.getObjectById(companyId, true);
List employees = (ArrayList) company.getEmployees();
Iterator it = employees.iterator();
while (it.hasNext())
{
Employee person = (Employee)it.next();
String name = person.getName();
Address address = person.getAddress();
Skills level = person.getLevel();
float salaire = person.getSalaire();
// ...
}

listing 9

Query query = pm.newQuery(Company.class);
query.declareParameters("String compName, String empName");
query.declareVariables("Employee emp");
String filter = "(name == compName) && (employees.contains(emp) && emp.name == empName)";
query.setFilter(filter);

Collection companies = (Collection) query.execute("boite", "Paul");
Iterator it = companies.iterator();
while (it.hasNext())
{
company = (Company)it.next();
List employees = (ArrayList)company.getEmployees();
Iterator it = employees.iterator();

while (it.hasNext())
{
Employee person = (Employee)it.next();
// ...
}
}

VII - Conclusion

La spécification JDO, activement soutenue par Sun, permet de faire persister des objets Java sur différents supports et de manière transparente pour l'utilisateur. En outre, celui-ci ne manipule que du Java et ne doit plus s'imprégner de langages de requêtes particuliers. En modifiant quelques paramètres dans un fichier de propriétés, l'auteur d'une application peur la faire migrer vers un tout autre support sans jamais modifier le code source. Les applications JDO s'intègrent donc parfaitement dans différents environnements Java, aussi bien dans les applications classiques que dans les applications J2EE. La gestion de cache offerte par les différentes implémentations autorise souvent des performances très supérieures aux bases de données relationnelles courantes utilisées seules. Le facteur couramment constaté est de 1 pour 10, mais peut se révéler plus important.
Grâce à sa faible empreinte mémoire, elle peut fonctionner là où aucune autre solution de persistance à haute performance n'est possible comme dans les environnements embarqués. On devine toutefois, suivant le vieil adage que plus c'est simple à utiliser côté client plus c'est sophistiqué côté serveur, qu'il s'agit d'une technologie de pointe ultrasophistiquée, probablement du top niveau du génie logiciel à l'heure actuelle.

VIII - Screenshots

Nos exemples reposent sur l'emploi d'une base de données MySQL.

Les quelques ouvrages sur JDO disponibles vous permettront de découvrir tous les secrets de cette technologie.

LIBeLIS propose une implémentation efficace et gratuite de JDO.

TJDO se targue de fonctionner sans aucun problème avec de nombreux SGBDR.

JDOCentral, le rendez-vous de tous les développeurs intéressés par JDO.

Sun Microsystems soutient activement JDO qui est soumis au Java Community Process.

Les éditions O'Reilly publient un ouvrage sur le sujet.

SolarMetrics propose une solution JDO pour les entreprises.

La solution Open Source Castor ne suit malheureusement pas les spécifications officielles.

Avec JDO, toutes vos angoisses sur les méthodes de stockage des objets sont envolées !

IX - Bibliographie

Java Data Objects, de Robin M. Roos, chez Addison Wesley

X - Liens

- Sources de l'application

- Spécification JDO de Sun

- JDO Central

- La FAQ JDBC, JDO, Hibernate

- Utilisation de JDO sur une base de données relationnelle, par Benoit Moussaud

- Utiliser JDO dans une application web avec JPox et Tomcat , par Benoit Moussaud