Java Data Objects (JDO) versus Microsoft ObjectSpaces (OS)

 

La couche de persistance ou de données est sûrement la partie la plus stratégique et la plus complexe d'un projet. C'est à cet endroit que sont confinées l'ensemble des entités de l'application au cœur du métier de l'entreprise (Facture, Comptes, ....). Mais c'est aussi et surtout dans cette couche que surviennent la plupart des problèmes liés aux performances : goulets d'étranglement, requêtes longues, transactions, ...

A travers les années et avec l'apparition des architecture n-tiers, cette couche a considérablement évolué pour aujourd'hui laisser place à une approche véritablement objet.  Il est bien connu que le monde relationnel et le monde objet ne font pas très bon ménage, et ce pour diverses raisons. Non seulement la granularité des tables n'est pas nécessairement équivalente à celle des objets, mais l'approche et la conception orientée données d'un coté ne trouve pas d'analogie formelle de l'autre. Bref, tout cela se passe comme si SGBDR et Objets devaient coexister malgré leur différences.

Avec l’expérience, nous savons aujourd'hui qu'il est possible de masquer à travers des objets toute la structure d'une base relationnelle. Cette approche a pour nom le mapping Objet/Relationnel. Pour ce faire, il existe trois types d'approches :

·        Réaliser cette opération manuellement de manière spécifique en utilisant des modèles de conception éprouvés (Design Patterns)

·        Utiliser des outils du marché tels que les EJB (Enterprise JavaBean) ou les outils de mapping objet/relationnel (JDO, ObjectSpaces, TopLink, CocoBase, …)

·        Faire un mélange des deux premières approches en optimisant manuellement les parties sensibles (gros volumes, ...)

Nous ciblerons dans cet article la deuxième et la troisième approche à travers deux Framework promis à un bel avenir : Java Data Objects (JDO) et Microsoft ObjectSpaces. Pourquoi avoir choisi ces deux là ? Car leur modèle de développement respectifs est très proche, mais aussi et surtout parce qu'ils représentent chacun à leur manière les deux architectures prédominantes du moment : Java et .NET.

Toute comparaison n'a de pertinence que lorsqu'elle basée sur un ensemble de critères communs. Nous avons choisi pour cet article les critères qui avaient le plus d'intérêt pour les applications d'entreprises et qui impactent directement la qualité d’un produit :

1.     Le modèle de développement

2.     Le mapping objet/relationnel

3.     La gestion des transactions

4.     Le langage d'interrogation

5.     Le cache d'objets

Pour chaque critère nous analyserons de part et d'autre leur implémentation tout en focalisant notre attention sur leur différences respectives. Toutefois, il est important de prendre en compte le fait que ObjectSpaces est un produit en cours de développement, il évoluera probablement à l'avenir et il convient donc de prendre avec précaution les conclusions qui suivront. Enfin, JDO est une spécification émise par Sun et destinée à être implémentée. C’est pourquoi, dans cet article, nous avons réalisé nos tests avec une de ces implémentations : Sun JDO-Ri qui est loin de représenter la qualité d'outils tels que Castor (ExoLab), Kodo ou LiDO (Libelis).

Le modèle de développement  

Le modèle de développement consiste à proposer un socle technique pour l'implémentation des objets persistants. Cela comprend la manière d'écrire un objet persistant mais aussi les éventuelles contraintes liées aux interfaces du Framework.

Sur cet aspect, JDO et ObjectSpaces adoptent la même démarche générale. Prenons un exemple concret avec le célèbre exemple de la Facture constituée de ces lignes.

 


Première remarque : ObjectSpaces impose une abstraction de la classe métier (mot clé abstract) alors que JDO, lui, autorise le client à fournir une implémentation des méthodes. En d’autres termes, le Framework ObjectSpaces génère l’implémentation et JDO réalise une introspection du code client afin d'extraire les informations dont il a besoin. Nous pourrions à priori penser que JDO ne génère aucun code, mais détrompez-vous. Il faut bien comprendre que le rôle d’un Framework de persistance est avant tout de stocker des objets suivant un type prédéfinit et contraint. Or, le type Facture précédent est inutilisable tel quel car il n’est pas dissociable techniquement des autres objets. C’est pourquoi, JDO, mais aussi ObjectSpaces, auront pour tâche de générer à l’exécution toute l’implémentation de ces objets afin de les insérer dans le socle technique du Framework. Cette opération est appelée enhancement ou extension. L’intérêt de cette technique consiste à masquer totalement au client le fait qu’il manipule une base de données ou un fichier XML et à générer de manière transparente toute la tuyauterie nécessaire à l’invocation des requêtes liées au type de stockage (SQL, XML, ... ).

Voyons de plus près le type d’objet manipulé par JDO :



Vous remarquerez que l’objet généré n’a plus rien à voir avec l’objet initial. Vous comprenez aisément qu’il est peu souhaitable d’imposer à un développeur d'implémententer une telle classe pour chaque objet métier. L’intérêt de l’approche par génération de code est de faire prendre en charge par le Framework cette lourde tâche.

Mais attardons nous sur les API JDO une fois les objets étendus. Le schéma suivant nous illustre la philosophie générale de l’API. Tous les objets persistants sont du type PersistenceCapable. Chaque entité est gérée à l’aide d’un StateManager chargé de mettre à jour l’objet en cas de modification mais aussi de synchroniser son état avec la base sous-jacente. Le StateManager est de toute évidence l’un des objets les plus important du Framework. Il possède toute l’intelligence liée au chargement et à la sauvegarde des données en mémoire. L’ensemble de ces classes est orchestrée par le PersistenceManager dont le rôle est d’intéropérer avec le type de stockage (SQL, XML, …).

Après extension, toutes les méthodes nécessaires au cycle de vie de l’objet sont créées. Lors d’une opération quelconque de l’utilisateur visant à manipuler l’état d’une entité, le conteneur JDO ou ObjectSpaces prendra soin d’invoquer au préalable les différentes méthodes de notification présentes dans l’objet (onCreate(), …).

 


 

Pour changer le type de stockage, il suffit d’appliquer une extension destinée à un autre Persistence Manager (XML), et le tour est joué !. Pour résumer, le JDO Persistence Manager joue le rôle de Factory d’objets du type PersistenceCapable que le client manipule directement de manière transparente.

                        PersistenceManager pm = pmf.getPersistenceManager();

Facture p = new Facture(“Dupont”, new java.util.Date());

pm.makePersistent(p);

 

Concernant ObjectSpaces, la démarche est légèrement différente même si la philosophie générale reste très proche. Contrairement à JDO qui mentionne dans la spécification officielle ce que doit contenir l’objet généré, ObjectSpaces est une implémentation et, à ce titre, l’investigation s’avère plus délicate. N’en déplaise à Microsoft et pour la bonne cause (;-)), nous avons décompilé les sources du Framework ObjectSpaces à l’aide de l’outil Salamander (www.remotesoft.com). Les résultats ont confirmé le contenu de la documentation du produit et nous a permis d’en savoir beaucoup plus sur le contenu du Framework.

Tout d’abord, l’extension d’un objet métier est réalisée à l’aide du compilateur C# suivant le principe de la génération dynamique de code (cf article "génération dynamique de code"). Une fois la classe étendue, elle devient du type IObjectSpace qui peut être comparée à l’interface PersistenceCapable de JDO. Le Design Pattern utilisé ici ne fait aucun doute, c’est la Factory (Fabrique de classes), autre point de raccordement. Le schéma suivant nous résume le principe :


 


La première étape consiste à récupérer une instance de la Factory d’objets métier étendus de type IObjectSpace :

IObjectSpace os = ObjectSpaceFactory.CreateObjectSpace("source.xml");


Notez que le fichier source.xml sert à spécifier le type de Factory utilisée : XML, SQL, OleDb, Spécifique, etc … Ensuite, il nous suffit de demander la création d’un objet métier étendu à l’aide de la méthode CreateObject() prenant en paramètre la clé primaire définie comme suit :

Facture theFacture = (Facture)os.CreateObject(typeof(Facture), "1");

Contrairement à JDO, ObjectSpaces utilise des classes abstraites. L’utilisateur doit donc aussitôt après la création de l’objet initialiser les différents champs :

theFacture.ClientName = "DotNetGuru" ;

theFacture.Date = System.DateTime.Now ;


A ce stade, l’objet possède une identité et une instance en mémoire mais n’est toujours pas synchronisé avec la base. Il convient donc d'invoquer la méthode UpdateAll() du cache de la manière suivante :

os.UpdateAll() ;

Ce scénario, trivial en apparence, fait appel à l’ensemble de la chaîne d’exécution du Framework technique, de la création des instances à la mise à jour des données.

En résumé, vous remarquerez que ObjectSpaces et JDO adoptent une démarche relativement similaire à travers une conception centrée sur le Design Pattern Factory et une approche par génération dynamique de code.

Le Mapping Objet/Relationnel

Le mapping est la brique essentiel assurant la représentation objet du modèle physique. ObjectSpaces et JDO sont encore une fois très proche dans leur manière d’aborder les choses.

Concernant le mapping des types, JDO, de part sa nature, utilise les types et collections Java alors qu’ObjectSpaces se base sur le CTS (Common Type System) de .NET.

Si en règle générale, ces Framework proposent un mapping 1 objet représente 1 table, dans la pratique cela tend à dégrader les performances et impose pour référence le modèle physique (granularité différente, …). C’est pourquoi, JDO et ObjectSpaces à travers l'interface DataSpace proposent un mécanisme d'extension de la persistance afin d'enrichir le comportement de base du Framework. Attardons nous sur les mécanismes et les outils proposés de part et d’autres pour concevoir ce mapping.

JDO et ObjectSpaces utilisent tous les deux XML comme format du fichier destiné à décrire le mapping Objet/Relationnel. Chacun, à sa manière, formalise ce mapping sous la forme de DTD pour JDO et de Schemas pour ObjectSpaces. 


 

Concernant les outils, la différence fondamentale provient du fait que ObjectSpaces propose à travers Visual Studio .NET un mapper graphique permettant de générer le fichier XML précédent. Ce mapper est en fait un plug-In tirant partie de l’interface de conception XML déjà existante afin de générer un schéma au format ObjectSpaces.

La gestion des transactions

La gestion des transactions prend une part importante dans chaque Framework. Ainsi, les objets peuvent être modifiés ou supprimés dans le cadre d’une seule et même transaction globale avec ou sans intervention de l’utilisateur. JDO dissocie deux types de gestion :

·        Transactions manuelles : le client spécifie explicitement la démarcation via les ordres pm.begin(), pm.commit() et pm.rollback()

·       Transactions gérées automatiquement : lorsqu’une méthode d’un objet métier est appelée, le conteneur JDO effectue les appels commit() ou rollback()

De plus, JDO utilise deux modes de verrouillage à travers les transactions :

·        Verrouillage optimiste (optimistic transaction management) : Aucun verrouillage d’enregistrements n’est opéré. Au moment du commit() une comparaison des instances actives en mémoire et de la base de données est réalisée et en cas d’incohérence, une exception est levée. Le client décide ensuite de la marche à suivre.

·       Verrouillage pessimiste (pessimistic transaction management) : Utilise le verrouillage classique d’enregistrement lors des accès concurrents.

ObjectSpaces adopte une démarche similaire concernant le mode de verrouillage du fait de l’utilisation d’un objet DataSet sous-jacent. L’utilisateur a aussi la possibilité de gérer explicitement la démarcation via les ordres ObjectSpace.BeginTransaction(), ObjectSpace.CommitTransaction() et ObjectSpace.RollbackTransaction().

En résumé, JDO et ObjectSpaces proposent les mêmes fonctionnalités avec un léger avantage pour JDO qui met l’accent sur une gestion plus poussée de ces transactions avec le support du protocole XA dans le cas de conteneurs distribués. 

Les langages d’interrogation

JDO adopte à travers l’interface Query le langage JDOQL (JDO Object Query Language) suivant plusieurs objectifs :

·        La neutralité par rapport au langage de la base : le langage sous-jacent peut être du SQL, du OQL ou n’importe quel système spécifique (mainframe, …)

·        L’optimisation : il doit être possible de profiter de la puissance des langages sous-jacents tel que SQL pour étendre l’interface Query (requêtes complexes, jointures, …).

·        L’adéquation avec les environnements n-tiers : il doit être possible d’effectuer des requêtes entièrement en mémoire ou déléguer leur exécution à un système tiers.

·        Le support des gros volumes de données : L’interface Query doit gérer le fait qu’un résultat renvoie de gros volumes d’enregistrements de manière optimale, c’est à dire sans instancier au préalable d’objets.

·       Les requêtes pré-compilées : il doit être possible de pré-compiler une requête afin d’optimiser les appels ultérieurs.

Quant à ObjectSpaces, le langage OPath est à l’honneur. Ce langage propriétaire, tout comme JDOQL d’ailleurs, permet d’effectuer des recherches en utilisant un formalisme proche d’OQL. Nous vous invitons à parcourir l’article de DotNetGuru à ce sujet.

Le cache d’objets

Le cache d’objets constitue la plus value d’un Framework de persistance. L’avantage du cache est de proposer à un utilisateur la manipulation des instances en mémoire afin d’éviter le surcoût lié à l’exécution d’une requête. Cependant, le cache d’objets s’avère pertinent dans un cas bien précis : lorsque les données sont en lecture seule où lorsque la majorité des instances ne sont pas amenées à être modifiées.

ObjectSpaces et JDO proposent tous les deux un cache transparent avec, pour JDO, la possibilité de spécifier explicitement l’éviction de certaines instances non utilisées dans le cache à l’aide de la méthode pm.evict(Object persistenceCapable). A l’heure où nous écrivons ces lignes, ObjectSpaces ne fournit pas ce type de méthode, l’interface IObjectCache comprend uniquement les méthodes Update() et Resync().

La spécification de JDO ne mentionne pas explicitement si le cache doit être distribué ou local, cette opération est laissée à l’appréciation du provider JDO. Toutefois, le développeur a la possibilité de bénéficier des services du Framework EJB (Distribution, Sécurité, Synchronisation, ...) en association avec le cache d'instances JDO. D'ailleurs, la spécification décrit de manière bien précise la démarche à suivre.

Quant à ObjectSpaces, le produit actuel ne propose pas de cache distribué. Les équipes de Microsoft ont déjà fort à faire pour stabiliser le produit et décliner des versions compatibles avec d'autres bases de données (Oracle, Sybase, MySql, ...).

Conclusion

JDO et ObjectSpaces sont des produits très similaires à plusieurs égards. L’approche utilisée de part et d’autre consiste à masquer l’ensemble des opérations de création, modification et suppression des instances à l’aide du Design Pattern Factory. De plus, l’impact du Framework concernant les entités manipulées est très faible du fait de la transparence induite par l’extension de code (génération dynamique). L’objet Facture initial se transforme en objet technique PersistenceCapable sous JDO et en IObjectSpace sous ObjectSpaces. Aussi, cet article nous prouve bien qu’il existe de multiple façons de concevoir un Framework de persistance, encore faut-il s’entendre sur un langage de requête commun. Enfin, ObjectSpaces et JDO nous démontrent une chose, l’avenir appartient au Framework qui feront la part belle au couplage faible entre objets métier et API de persistance. Finalement, tout l’inverse de ce que propose aujourd’hui les EJB entités…

 

Auteur : Sami Jaber

Copyright : DotNetGuru Ó 2002

Ressources

Spécifications officielles de JDO par Sun : JSR 000012 (release)

Génération dynamique de code (DotNetGuru)

La rumeur ObjectSpaces (DotNetGuru)

Article développeur référence sur JDO (Didier Girard)