Cours architecture J2EE avec exemples
...
Le modèle relationnel, mis au point dans les années 1970 à la suite notamment des travaux théoriques d’ Edward F. Codd†, a constitué une véritable révolution pour la conception des systèmes d’information. Durant les 30 dernières années, les bases de données relationnelles se sont largement répandues et imposées au sein des organisations et entreprises de toutes tailles. La simplicité et la puissance intrinsèques au modèle relationnel, et le support massif des géants du logiciel comme Oracle, IBM ou Microsoft ont contribué à ce mouvement. En gérant avec succès de nombreuses applications critiques, les systèmes de gestion de bases de données relationnelles ont prouvé leur fiabilité et gagné la confiance des directions informatiques.
Parallèlement, la technologie objet voit le jour au début des années 80 et laisse entrevoir de nombreux espoirs : ses capacités de modélisation très évoluées et son haut degré d’abstraction permettent une conception plus riche, plus flexible, plus modulaire, et plus évolutive. Non seulement la conception d’applications se trouve facilitée, mais il est désormais possible d’envisager la création de composants logiciels réutilisables. Dès lors, la technologie objet est en mesure de garantir un meilleur retour sur investissement par rapport au relationnel, lorsqu’elle est correctement utilisée. Néanmoins, eu égard notamment à sa complexité, à la mauvaise utilisation qui en a souvent été faite, et au manque de maturité des outils et des bases de données orientées objet, ce modèle n’a pas remporté le succès escompté. L’arrivée de nouveaux standards comme OQL (Object Query Language) au début des années 90 et de bases de données objet dignes de ce nom sur le marché n’a pas inversé la tendance : les gestionnaires de bases de données objet n’occupent que des marchés de niche (domaine scientifique, domaine des télécoms, etc.).
Dans la plupart des cas, les entreprises ont préféré renoncer aux avantages de l’objet et continuer à utiliser le modèle relationnel. Les raisons invoquées le plus fréquemment sont les suivantes :
- l’importance de l’existant relationnel : les investissements massifs réalisés dans les bases relationnelles (compétences, outils de développement, administration, etc.) ne sont parfois pas encore amortis, et l’hésitation des entreprises à faire le pas vers l’objet est légitime ;
- le risque technologique : les technologies objet sont relativement complexes à mettre en oeuvre, et le saut culturel engendré par son adoption présente un risque important. Les middleware d’utilisation des technologies objet (ORB, bases de données objet, etc.) remettent parfois en cause une partie de l’existant. Les entreprises restent également dubitatives quant à la maturité et à la fiabilité de la technologie objet, notamment en ce qui concerne les bases de données objet.
Cependant, le développement du Web a donné un second souffle aux technologies objet. Les entreprises sont en effet contraintes d’intégrer le Web à leurs systèmes d’information, et les standards de facto permettant de réaliser ce type d’architecture, J2EE et Microsoft DNA (maintenant .Net) fonctionnent à base de technologies objet. L’utilisation du modèle objet pour la conception d’entités métier (client, contrat, etc.) pose des problématiques épineuses, notamment celles de la persistance des attributs des objets et des transactions objet. En effet, à la différence des objets techniques (interface utilisateur, requêtes, etc.), les objets métier doivent être dotés de deux propriétés étroitement liées : persistance et transactionnel.
En parallèle, l’arrivée de nouvelles méthodes de notation comme UML et d’outils de conception associés fait la part belle à l’objet. Les entreprises se trouvent donc confrontées à un choix délicat : comment intégrer les technologies objet dans leurs architectures ?
Dans le monde Java/J2EE, un certain nombre de technologies et de produits sont apparus pour répondre à ces besoins et proposer de véritables outils de modélisation par objets métier. Citons parmi eux les normes de Sun Microsystems : EJB Entité et, plus récemment JDO, ainsi que des produits spécialisés comme Oracle9iAS TopLink. Ces différentes normes et outils apportent des éléments de réponse au problème de la persistance, qui est une nécessité pour l’implémentation des modèles métier.
1.1 Présentation du modèle relationnel
1.1.1 Caractéristiques principales
Le modèle relationnel se distingue par ses capacités élaborées de gestion des données, parmi lesquelles les plus remarquables sont :
- sa capacité à stocker de manière durable et fiable de très gros volumes de données
- la possibilité d’organiser les données de façon structurée et de définir des relations simples entre ces données
- la gestion de l’intégrité des données et des accès et mises à jour concurrentes
- la possibilité de manipuler les données de façon très rapide et sélective, à travers des requêtes exprimées à l’aide d’un langage spécifique (DML – Data Manipulation Language)
Modélisation des entités
Dans le modèle relationnel, les données sont structurées selon une organisation bi-dimensionnelle. Les informations sont modélisées sous forme de tables (ou relations), qui se présentent sous la forme d’ensembles de tuples. Un tuple est lui-même constitué d’un ensemble d’attributs nommés, typés, et valués. On parle également de lignes ou d’enregistrements pour désigner les tuples, et de colonnes ou de champs pour désigner les attributs.
id lastname firstname birthdate address zip city
1 Durant Jean 01-MAR-70 22 rue Rouge 75004 PARIS
2 Dupont Bill 22-NOV-75 23 rue du Fond 13008 MARSEILLE
.... .... .... .... .... .... ....
Les seules opérations possibles sur un tuple donné d’une table sont :
- la création d'un tuple
- la lecture de la valeur d'un attribut de ce tuple
- la modification de la valeur d'un attribut de ce tuple - la suppression de ce tuple
Gestion de l’identité des données
Les tuples stockés dans les tables ne peuvent être identifiés qu’à partir des valeurs des différents attributs qui les composent. Afin de permettre d’identifier de manière unique chaque tuple, il est possible de désigner un ou plusieurs attributs comme clé primaire. Le système exige (et garantit) alors que les valeurs de clé primaire de chaque tuple soient distinctes.
De ce concept de clé primaire dérive celui de clé étrangère, qui permet de gérer les relations entre plusieurs tables : un champ de clé étrangère est un attribut qui contient la clé primaire d’un enregistrement d’une autre table. De cette manière, les tuples d’une table peuvent référencer des tuples d’autres tables.
Par exemple, on peut modéliser une table Orders qui contient les informations relatives aux commandes des clients. Outre sa clé primaire propre (id), chaque commande comporte également un champ de clé étrangère (customerid) qui fait référence à la clé primaire du client qui a passé cette commande :
Id customerid orderdate status
2 1 15-DEC-02 processing
3 2 17-DEC-02 processing
.... .... .... ....
Un langage puissant de manipulation des données
Le modèle relationnel offre un outil concis et puissant pour la manipulation des données structurées sous formes de tables: il s'agit de l'algèbre relationnelle. Les opérations de l'algèbre relationnelle (projection, sélection, jointure, etc.) sont très synthétiques. En pratique, l'algèbre relationnelle s'utilise via divers langages, le plus répandu étant le langage SQL (Structured Query Langage).
Voici quelques exemples de requêtes SQL, qui illustrent la concision et la puissance de ce langage :
Opération à réaliser Requête SQL
Sélection des clients (nom, prénom,
adresse, ville) qui habitent à Marseille SELECT firstname, lastname, address, zip FROM Customers
WHERE city = ‘Marseille’
Sélection des commandes faites par le
client de numéro 1 (à l’aide d’une jointure entre les tables Customers et Orders) SELECT ordername
FROM Customers INNER JOIN Orders ON Customers.id = Orders.customerid WHERE Customers.id = 1
Mise à jour du champ ‘status’ à la valeur ‘shipped’ pour l’enregistrement de la table Commande dont le champ id (clé primaire) égale 1. UPDATE Orders SET status = ‘shipped’ WHERE id = 1
Des capacités d’abstraction limitées
1.1.2 La gestion transactionnelle
Généralités sur les transactions
Dans un système de base de données, une transaction est une séquence d'opérations élémentaires (lecture ou mise à jour de données sur une ou plusieurs tables) exécutée comme une seule opération indivisible :
- si l’ensemble des opérations est mené à terme avec succès, les modifications effectuées deviennent permanentes — la transaction est validée.
- si l’une des opérations échoue, les modifications partielles déjà effectuées sont annulées et l’état initial de la base de données est restauré — la transaction est annulée.
Par exemple, dans une application bancaire, un virement de compte à compte peut être modélisé comme une transaction constituée de deux opérations complémentaires et indissociables:
- débit du compte source d’une somme X
- crédit du compte cible de la même somme X
Afin de maintenir l’intégrité des données bancaires, il est primordial de garantir que soit ces deux opérations seront exécutées, soit qu’aucune ne le sera. En aucun cas ne doit exister de situation dans laquelle seule l’une des deux opérations est exécutée. On peut également enrichir cette transaction en y ajoutant une 3ème opération, préliminaire : la vérification de la disponibilité des fonds pour le virement sur le compte source. Si le solde du compte source est insuffisant pour permettre le virement, alors on doit forcer l’annulation de la transaction.
Plusieurs transactions peuvent être exécutées de façon concurrente. Tant qu’une transaction n’est pas menée à son terme, les modifications qu’elle entreprend restent invisibles aux autres transactions qui s’exécutent. Par exemple, plusieurs transactions peuvent potentiellement mettre à jour de façon concurrente un compte bancaire : virement, encaissement ou paiement de chèque, etc.
- Atomicité : les opérations constituant la transaction forment une unité indivisible : soit l’ensemble des opérations est mené à terme, soit aucune opération n’est effectuée.
- Cohérence : les transactions font passer le système de base de données d’un état initial cohérent à un état final cohérent, dans le respect des règles d’intégrité référentielle définies sur la base de données (cependant, au cours de l’exécution de la transaction, ces règles peuvent être temporairement enfreintes).
- Isolation : les mises à jour effectuées au cours de l’exécution d’une transaction restent invisibles aux autres transactions. Les modifications ne deviennent visibles qu’une fois la transaction validée. En pratique cependant, cette propriété d’isolation peut être relaxée : les bases de données gèrent en effet plusieurs niveaux d’isolation des transactions, qui permettent de moduler le niveau de visibilité de données non validées entre transactions concurrentes.
- Durabilité : après l’aboutissement d’une transaction, les mises à jour effectuées deviennent définitives et ne peuvent plus être annulées.
Au sein des serveurs d’application, il existe un service particulier dédié à la gestion et à la coordination des transactions effectuées par les applications sur une ou plusieurs bases de données : le gestionnaire de transactions.
Gestion de la concurrence d’accès :
Pour assurer la gestion de la concurrence entre transactions simultanées, le système de gestion de bases de données a recours à des mécanismes de verrouillage sur les données en cours de modification. La pose d’un verrou par une transaction sur un ou plusieurs enregistrements empêche d’autres transactions de modifier ces données de façon concurrente. Typiquement, les bases de données offrent deux stratégies de verrouillage pour les transactions:
- mode partagé ou « optimiste » : durant l’exécution de la transaction, aucun verrou n’est posé sur les données mises à jour, qui restent accessibles aux autres transactions. A la validation de la transaction, juste avant la mise à jour, le système doit par conséquent vérifier que les données manipulées n’ont pas été modifiées entre-temps par d’autres transactions. Si les données sont intactes, la validation de la transaction s’effectue, avec pose d’un verrou en écriture juste pendant la phase de validation. Si les données ont été modifiées entre-temps par une transaction concurrente, la transaction est annulée et les modifications sont perdues. L’avantage de ce mode est que les données ne sont verrouillées que pendant des laps de temps très courts (phase de validation uniquement), ce qui favorise une haute concurrence d’accès. L’inconvénient principal est le risque d’échec des transactions et la perte des modifications qui peuvent se produire lorsque plusieurs transactions effectuent des mises à jour sur un même ensemble de données.
Les différents modèles transactionnels :
Le modèle de transactions que nous avons décrit correspond au modèle le plus simple : le modèle de transactions linéaires. Dans ce modèle, une transaction est une séquence linéaire d’opérations qui, soit sont toutes exécutées, soit ne le sont pas.
Ce modèle permet une plus grande modularité et une plus grande complexité dans la conception des applications transactionnelles. Cependant, ce modèle n’est pas encore aussi largement supporté par les gestionnaires de bases de données ou par les serveurs d’application que le modèle de transactions linéaires.
Les différents types de transactions :
On distingue également deux types de transactions, les transactions locales d’une part, et les transactions globales, ou transactions distribuées, d’autre part. La distinction entre les deux s’opère sur le nombre de participants à la transaction :
Dans une transaction locale, seuls deux participants sont impliqués: l’application effectuant la transaction, et la base de données sur laquelle les mises à jour sont effectuées.
Une transaction globale implique plus de deux participants : par exemple, une application effectuant des mises à jour sur plusieurs bases de données, ou bien plusieurs applications coordonnées entre elles pour une mise à jour sur une ou plusieurs bases de données. La mise en œuvre de ce type de transactions nécessite l’utilisation d’un protocole spécifique pour la coordination des opérations entre participants: le protocole de validation à deux phases. Au cours de la première phase, le gestionnaire de transactions demande à chaque participant s’il est en mesure de valider la transaction. Si tous les participants sont prêts, la seconde phase est déclenchée, et le gestionnaire de transactions intime l’ordre à chaque participant de valider la transaction. Dans le cas contraire, si l’un des participants indique qu’il n’est pas en mesure de valider la transaction, le gestionnaire de transactions donne l’ordre à chaque participant d’annuler la transaction au cours de la seconde phase.
Conclusion :
De ce fait, les transactions se révèlent indispensables pour la réalisation de systèmes d’information et d’applications de gestion complexes.
1.2 Présentation du modèle objet
1.2.1 Caractéristiques principales
Alors que le modèle relationnel est centré sur les données, le modèle objet lui repose sur l’intégration étroite entre les données et les traitements.
Ce modèle se distingue par le niveau élevé d’abstraction qu’il offre, ainsi que par ses capacités avancées de modélisation. Parmi les caractéristiques les plus remarquables de ce modèle, on notera :
- l’intégration étroite réalisée entre les données et les traitements appliqués à ces données, appelée encapsulation : un objet combine un état (ensemble des valeurs courantes des attributs de l’objet, qui sont des données typées, et de ses références, ou associations, vers d’autres objets) et un comportement (les opérations possibles sur ces attributs et associations) en une entité de haut niveau.
- la souplesse de modélisation des données: le modèle objet offre le support d’un très large spectre de types de données, et permet d’en définir facilement de nouveaux qui peuvent être combinés avec des types existants. Les deux principes sous-jacents sont l’héritage (qui permet la spécialisation des objets), et la composition ou agrégation (qui permet l’ association de différents objets).
- cette flexibilité au niveau de la modélisation des données se retrouve au niveau de la modélisation des traitements : le polymorphisme permet la spécialisation des traitements en fonction de la spécialisation des objets. De plus, il n’existe aucune restriction quant à la nature ou au nombre des traitements qui peuvent être définis sur les objets.
Modélisation des entités :
fig. 1 : Schéma d’un objet
Alors que le modèle relationnel limite les opérations pouvant être appliquées aux données à quatre primitives de base (création, lecture, mise à jour, destruction), le modèle objet permet de définir un nombre arbitraire d’opérations sur les données.
Identité vs . Égalité des objets
Contrairement aux tuples d’une relation, les objets ne disposent pas d’un concept de clé primaire permettant de les identifier de manière unique. L’identification d’un objet est simplement fonction de son adresse en mémoire. Ainsi, deux objets peuvent être égaux (les valeurs de leurs attributs sont égales) mais pas identiques (leurs adresses mémoire sont différentes). Ainsi, si l’identité objet est assurée par l’unicité des emplacements mémoire affectés aux objets, il n’existe aucun mécanisme permettant d’interdire l’égalité des attributs entre deux objets de même type comme cela est possible dans le modèle relationnel.
Gestion des relations d’association entre objets
Dans le modèle relationnel, des champs particuliers (clés étrangères), et parfois même des tables dédiées (« relations ») sont utilisés pour représenter les relations d’association entre plusieurs tuples.
Contrairement au modèle relationnel qui stocke des valeurs d’attributs de clés primaires pour établir les associations entre tuples, le modèle objet fait usage des références (l’identité des objets) pour maintenir les associations entre objets liés. Dans le cas de relations de type 1-n, une collection de références est maintenue dans un attribut de type vecteur, liste, ou tableau. Afin de récupérer les différents objets associés à un objet particulier, on emploie des algorithmes d’itération sur les collections et de parcours de graphe (traversée ou indirection de références).
Des fonctionnalités de manipulation très limitées
Historiquement, la technologie objet a manqué de fondements théoriques et de standards au niveau des techniques de manipulation. Les développeurs ont alors adopté des approches très analytiques pour manipuler les ensembles d’objets (traitements par itérations) assez peu efficaces. Des standards pour les bases de données objet, comme le langage OQL (Object Query Language), sont nés beaucoup plus tard et n’ont pas été aussi massivement adoptés que le SQL dans le monde des bases de données relationnelles. D’autre part, les langages de requêtage objet sont beaucoup plus difficiles à maîtriser que leurs homologues relationnels.
1.2.2 La problématique de la persistance objet
Si le modèle objet offre des capacités de modélisation et d’abstraction avancées, il est en revanche handicapé par une grave limitation : il se focalise exclusivement sur les aspects dynamiques de l’exécution des programmes, sans couvrir réellement la problématique de la persistance des objets :
Les objets représentent des entités, des informations qui peuvent être partagées par plusieurs applications, et qui peuvent avoir une durée de vie très longue, dépassant la durée d’une session applicative. Cependant, les objets n’existent qu’en mémoire ; par conséquent, une fois la session applicative terminée, les objets sont détruits, et les informations qu’ils contiennent disparaissent avec eux.
Par contraste, dans le modèle relationnel, les données sont persistantes : une fois que la structure du schéma relationnel a été définie, les données qui y sont ajoutées restent accessibles durablement, tant qu’elles ne sont pas explicitement supprimées. La durée de vie des données est donc totalement indépendante du cycle de vie des sessions applicatives qui les manipulent.
L’état d’un objet étant constitué de l’ensemble des valeurs de ses attributs, il suffit donc de sauvegarder l’état de ces différents attributs sur un support de stockage permanent pour être en mesure de reconstituer l’état de l’objet ultérieurement. Cependant, plusieurs considérations viennent compliquer la simplicité apparente de cette tâche :
- la question du type des objets : chaque objet appartient à une classe donnée ; il est donc nécessaire de sauvegarder les informations relatives à l’appartenance de l’objet à une classe particulière
- la question des références vers d’autres objets : au-delà des types scalaires simples, les objets peuvent avoir comme attributs des références vers d’autres objets ; la représentation d’un objet ne s’apparente donc pas à un tableau de valeurs, mais à un graphe de valeurs. Sauvegarder un objet implique donc la sauvegarde complète de ce graphe, c’est à dire la sauvegarde de tous les objets référencés par cet objet.
- la question de l’identité objet : comme nous l’avons indiqué, contrairement aux enregistrements de bases de données, les objets ne disposent pas d’une identité propre, en dehors de leur adresse mémoire à l’exécution. Pour la recherche et la restauration ultérieure des objets, il est nécessaire de leur associer des informations permettant leur identification.
Techniquement, il existe plusieurs approches pour implémenter la persistance objet:
- sérialisation : comme nous l’avons exposé, la représentation d’un objet s’apparente à un graphe. La sérialisation consiste à définir une méthode permettant de « mettre à plat » cette représentation afin de la sauvegarder comme un flux d’information uni-dimensionnel (sériel) dans un fichier. On peut définir et utiliser des flux binaires (fichiers binaires), ou textuels (fichiers XML par exemple).
- persistance en base de données objet : le problème de la persistance objet a donné lieu au développement d’un nouveau type de bases de données : les bases de données orientées objet. Contrairement aux bases de données relationnelles qui emploient des structures bi-dimensionnelles (les tables), les bases de données objet utilisent les mêmes représentations que les objets : des structures hiérarchiques (graphes).
Au-delà du mécanisme de persistance utilisé, il est également nécessaire d’implémenter la logique de persistance qui va permettre aux applications d’effectuer la sauvegarde et la restauration de l’état des objets manipulés. C’est le rôle de la couche de persistance (ou moteur de persistance). Le moteur de persistance expose une API aux applications pour la création, la mise à jour, et la recherche d’objets sur le support de persistance. A ce moteur de persistance, il est également nécessaire d’associer un outil de projection des objets sur le support de persistance : le rôle de cet outil est de fournir le moyen de définir les règles de transformation entre la représentation des objets en mémoire et la représentation de ces objets sur le support de persistance, avec la création des structures de données nécessaires.
La persistance objet est donc une problématique très complexe, qui nécessite la mise en œuvre de mécanismes spécifiques et sophistiqués.
1.3 Différences entre les modèles objet et relationnel
L’exposé des caractéristiques fondamentales des modèles relationnel et objet permet d’apprécier leurs grandes dissimilitudes, ainsi que les difficultés qui se posent lorsqu’on cherche à établir un pont entre ces deux mondes.
- dans le modèle relationnel, les données sont organisées de manière tabulaire, sous forme matricielle (lignes et colonnes). Dans le modèle objet, les données sont organisées sous la forme d’un graphe d’objets (ensembles d’attributs). Toute la difficulté du mapping objet/relationnel consiste à effectuer la traduction entre ces deux représentations.
- le problème des types de données : le passage d’une représentation objet à une représentation relationnelle implique souvent une adaptation des types de données, et même des conversions de types. Les types de données disponibles et les restrictions sur les valeurs acceptables diffèrent entre les modèles relationnel et objet.
- le problème de l’héritage : le concept d’arbre d’héritage est étranger au modèle relationnel. Il est cependant possible d’accommoder une hiérarchie de classes de plusieurs manières : par exemple, en créant une table pour chaque classe dans la hiérarchie, avec les attributs correspondants, ou bien en créant une seule table contenant l’ensemble des attributs de toutes les classes de la hiérarchie.
- le problème de l’identité objet : dans le modèle relationnel, l’unicité des tuples est garantie par l’utilisation de clés primaires. Par contraste, dans le modèle objet, l’unicité des objets est uniquement fonction de l’unicité des références mémoire. Cette gestion d’identité et de références peut très vite devenir complexe et source d’erreur, notamment dans le cas de relations circulaires entre objets. D’autre part, lorsqu’un objet est sérialisé vers un support de stockage, l’identité de l’objet est perdue. Pour remédier à ce problème, on doit munir chaque objet d’un attribut de clé primaire, et mettre en œuvre un mécanisme logiciel garantissant l’unicité des objets vis à vis des valeurs de ces attributs de clé primaire.
Ces différents obstacles expliquent en partie la très grande complexité de la problématique d’intégration entre les modèles relationnel et objet. L’expérience prouve que les considérations de maintien de l’intégrité des données entre les deux représentations, de gestion des transactions et d’optimisation de la gestion mémoire du graphe d’objets sont extrêmement difficiles à résoudre.