Rapport de fin de phase II - serveur campus des écoles hes

publicité
ISNet35 Rapport phase II
Cadre du projet
Ce projet à été déposé dans le centre de compétence Informatique de gestion et
systèmes (ISNet) dans le cadre du programme de la réserve stratégique de la HES-SO en
septembre 2001. Il est sous la responsabilité du Laboratoire de Technologie Objet de la
Haute Ecole de Gestion de Genève.
1.1. Rappel des objectifs du projet
Les objectifs du projet sont initialement définis ainsi :
A partir des classes Java obtenues à l'issue d'un processus d'Analyse et de Conception
• Concevoir un processus générique automatique d'implantation de la persistance
d'objets Java dans une base de données relationnelle-objet, offrant en particulier les
possibilités
¾ de gérer cette persistance de façon transparente pour le programme d'application
Java,
¾ de permettre l'accès aux données représentées par les objets Java par des
applications quelconques en préservant l'intégrité des données.
• Elaborer une démarche méthodologique permettant de comparer le résultat de ce
processus aux technologies existantes.
• Réaliser et valider un prototype implantant effectivement ce processus générique.
• Rédiger une publication qui sera soumise à une revue spécialisée.
1.2. Objectifs de la phase II
La phase II, intitulée Réalisation et validation du prototype est définie initialement par les
objectifs suivant :
• Implanter effectivement le prototype décrit par le cahier des charges obtenu à l’issue
de la première phase;
• Valider l’outil obtenu en développant au moins deux applications métier types selon
les deux processus de développement cités ; les applications seront à priori
différentes exploiteront un ensemble de données commun représentées par les trois
technologies (relationnelle pure, relationnelle-objet, vue relationnelle-objet)
• Appliquer la démarche méthodologique d’évaluation ;
• Produire un document de synthèse comprenant :
¾ Les résultats et conclusion de l’étude comparative
¾ Une description du processus de réalisation et de validation mis en œuvre
22.06.2005
1/22
1.3. Document
Le présent document est le rapport de synthèse du travail du groupe de compétence
ISNet-Ne de la Haute école Neuchâteloise en fin de phase II du projet, son contenu devra
s’intégrer dans le cadre global du projet.
Suite aux résultats de la phase I, l’activité de l’équipe neuchâteloise s’est principalement
orientée sur la réalisation d’un prototype de génération de script de création d’objets
persistant dans la base de données à partir de leurs spécifications contenues dans un
fichier XMI. Le modèle de persistance retenu est le modèle relationnel-objet d’Oracle.
Prototype de générateur SQL-DDL
1.4. Introduction
Le prototype que nous avons réalisé permet de générer le script SQL-DDL (Data
Defintion Language) pour une base de données relationnelle (le SGBD cible retenu lors
de l'étape I est Oracle) à partir d'un fichier XMI contenant un modèle de domaine UML
(seul les diagrammes de classes sont pris en considération).
Le prototype est loin d'être une solution de production. Notre objectif était de monter la
faisabilité d'une transformation XMI-UML vers SQL-DDL. Un certain nombre de choix ont
été implémentés sans forcément utiliser la solution la plus propre. Par exemple, nous
utilisons un stéréotype pour gérer les contraintes NOT NULL au lieu d'utiliser la
multiplicité à disposition pour les attributs UML. Nous avons également simplifié certaine
partie de la mise en correspondance afin de respecter les délais qui nous étaient
initialement fixés. Par exemple, pour le moment, les associations doivent être navigable
que dans un sens. De ce fait, l'analyse du fichier XMI s'en trouve facilitée puisque nous
avons contourné le problème posé par les associations bi-directionnelles (graphe
cyclique).
Les critères du génie logiciel et les principes de la conception orientée objet n'ont pas
forcément été respectés dans le cadre de ce travail. En effet, nous avons réutilisé
quelques classes issues d'un autre travail sur la génération de code SQL-DDL qui
n'étaient initialement pas prévues pour être intégrées à ce projet. De plus, nous avons
privilégié la fonctionnalité sur la bonne odeur du code source (au sens XP du terme).
Dans le cas d'une implémentation de production de notre prototype, un important travail
de refactoring sera nécessaire pour obtenir une solution de qualité.
1.5. Parsing XML
Dans le cadre du développement de ce prototype, nous avons utilisé l’API de Sun JAXP
(Java API for XML Processing) pour effectuer le parsing des fichiers XMI. Nous avons
retenu la méthode de parsing de type DOM (Document Object Model). Par manque de
temps, nous n’avons pas pu étudier l’autre alternative orientée événements qui s’appelle
SAX (Simple API for XML).
De toute façon, il nous semble absolument évident qu’une version de production du
générateur devrait être construite sur la base de la norme Xpath du W3C. Etant donné,
les évolutions futures d’XMI (la version 2.0 d’UML devrait bientôt être publiée), il faut
22.06.2005
2/22
absolument minimiser la maintenance nécessaire concernant les changements liés à
l’organisation des fichiers XMI sur laquelle nous n’avons aucune influence. Nous avons
déjà connu de telles difficultés car Rational Rose supporte XMI en version 1.1 et Poseidon
for UML en version 1.2. De ce fait notre générateur ne fonctionne qu’avec des fichiers
XMI générés à partir de Rational Rose.
L’avantage de JAXP est de permettre au développeur de ne pas utiliser de classes
spécifiques à un parseur X ou Y mais de travailler de façon abstraite avec n’importe quel
parseur. Le choix du parseur se fait au niveau du système et l’instanciation de celui-ci est
délégué à la classe DocumentBuilderFactory dans le cas du DOM.
1.5.1. Parsing DOM
La manipulation d’un fichier XML avec le DOM se fait au moyen des interfaces Document
et Node. Pour rappel, le DOM propose une méthode d’accès au contenu d’un fichier XML
de type arborescente. A partir du nœud racine, il est possible d’accéder aux nœuds
enfants et ainsi de suite.
L’interface Node disposent des méthodes suivantes :
Un nœud est une entité polymorphe qui peut-être un ELEMENT, un ATTRIBUTE, ou
encore du texte. La méthode getNodeType() permet de connaître le type exacte du
nœud.
Comme nous l’avons dit plus haut, en utilisant JAXP, le code Java est indépendant du
parseur utilisé. De ce fait, il faut recourir à une fabrique pour l’instanciation de celui-ci.
DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
Une fois la fabrique instanciée, il est possible de paramétrer celle-ci. Le paramétrage
étant effectué, il ne reste plus qu’à demander la création d’une instance de parseur. Dans
le contexte du DOM, un parseur s’appelle un DocumentBuilder.
DocumentBuilder b = bf.newDocumentBuilder();
Le parsing ne s’effectue pas automatiquement. Il faut pour ce faire appeler la méthode
parse() et lui indiqué en paramètre le nom du fichier XML à traiter. Cette méthode
retourne une instance conforme à l’interface Document qui sert de point de départ pour
la manipulation de l’arbre qui vient d’être construit en mémoire sur la base du fichier
XML.
Document doc = b.parse("fichier.xml");
A partir du document, on peut obtenir le nœud racine au moyen de la méthode
getDocumentElement().
22.06.2005
3/22
Element root = doc.getDocumentElement() ;
L’interface Element est une interface qui hérite de Node qui offre des méthodes
spécifiques au traitement des balises XML (<!ELEMENT> dans une DTD).
A partir de maintenant, puisque nous sommes en possession du nœud racine du
document, JAXP ne nous est plus d’aucune utilité puisque c’est à nous d’écrire le code
nécessaire pour traiter le document XML selon nos propres besoins. Il s’agit de s’appuyer
sur la méthode getChildNodes() de l’interface Node pour réaliser ces traitements.
1.5.2. Parsing XPath
Nous présentons dans la seconde partie de ce rapport l’utilisation d’une base de données
Oracle pour l’interrogation d’un document XML au moyen de la norme XPath. Il nous
semblait cependant intéressant de montrer qu’il est également possible d’utiliser ce
mécanisme depuis un programme Java en utilisant un moteur de transformation XSL.
Pour ce test, nous avons choisi d’utiliser l’outil Xalan de la fondation Apache. Nous
n’avons malheureusement pas eu le temps de regarder si JAXP offrait une couche
d’abstraction pour ce genre d’outil comme c’est le cas pour les parseurs DOM et SAX.
En nous basant sur l’exemple de manipulation présenté ci-dessous dans la partie sur
XPath et Oracle, voici la requête XPath qui devrait nous retourner le nom de la classe
contenue dans le fichier XMI :
/XMI/XMI.content/UML:Model/UML:Namespace.ownedElement/UML:Class/@name
La première étape consiste à créer une instance compatible avec l’interface Document
(voir la section ci-dessus Parsing DOM) qui sera ensuite transmise au moteur de
recherche XPath.
DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
DocumentBuilder b = bf.newDocumentBuilder();
Document doc = b.parse("demo.xmi");
Il ne nous reste plus qu’à demander au moteur de recherche XPath d’effectuer notre
requête. Celui-ci retourne, en fonction de la méthode utilisée, une instance de Node ou
une collection de Node.
Node n = XPathAPI.selectSingleNode(doc, request) ;
La variable request est une chaîne de caractères contenant la requête XPath à appliquer
au document XML représenté par la variable doc. Le nœud reçu en réponse à notre
requête peut maintenant être traité au moyen des méthodes de l’interface Node comme
si nous avions effectué laborieusement le parsing de façon traditionnelle au moyen d’un
arbre DOM.
Grâce à l’utilisation de XPath pour le parsing de fichier XMI, il devient possible d’assurer à
moindre frais l’évolutivité de l’application en regard des changement de la spécification
XMI et/ou UML. Le chemin d’accès pourrait être prédéfinis dans un fichier de
configuration afin de pouvoir être changés sans nécessiter la recompilation de
l’application.
L’utilisation de XPath peut se faire soit en utilisant un outil comme Xalan ou alors en
utilisant les fonctionnalités d’une base de données XML comme Oracle (voir prochain
chapitre). Dans ce deuxième cas, les tests réalisés ont montré un certain degré
d’immaturité dans la solution proposée par Oracle c’est pourquoi la solution Xalan
22.06.2005
4/22
pourrait bien être une alternative intéressante. De plus l’utilisation d’un outil comme
Xalan permet d’offrir une solution indépendante d’un quelconque SGBD du marché.
Vous trouverez des plus amples informations sur XPath dans la deuxième partie de ce
document ainsi que sur le site du W3C (http://www.w3.org/TR/xpath). Xalan est
disponible sur le site XML de la fondation Apache (http://xml.apache.org/xalanj/index.html).
1.6. Implémentation du générateur SQL-DDL
1.6.1. Présentation générale
De façon très simplifiée, le générateur fonctionne grâce à la collaboration des objets de
trois classes. Une instance de la classe XmiAnalysator lit un fichier XMI et récupère les
méta-données dans un conteneur de type XmiData. Ces méta-données sont ensuite
transmise à une instance de ScriptGenerator. Celui est responsable de la transformation
des méta-données au format relationnel (ajout de l’attribut clé étrangère par exemple).
La classe SQL_DDLCreator est la façade du système. Elle coordonne l’analyse du fichier
XMI et la production du fichier SQL-DDL correspondant.
Il est possible de lancer l'application en mode ligne de commande ou en mode graphique
(Swing).
22.06.2005
5/22
XmiAnalysator
(from hegne)
ScriptGenerator
(from hegne)
XmiAnalysator()
XmiAnalysator()
generat eXmiData()
getClas s()
getClas sAttributs()
getClas sAttributType()
getA ssociations()
saveA ssociations()
getB alise()
seekClassName()
getClas sAs sociations()
getS tereoty pe()
getConstraints()
ScriptGenerator()
ScriptGenerator()
generateRelationalDataXmi()
analyseXmiConstraint()
analyseRelationalData()
displayConstraint()
schearchNumtable()
writeScript()
seekFK()
correct()
XmiData
(from hegne)
Modelname : Logical View::java::lang::String = ""
1.6.2. Présentation détaillée
ch.hegne.SQL_DDLCreator
Cette classe s’occupe de mettre en place la génération du script SQL-DDL à partir de
l'analyse d'un fichier XMI. La méthode createTable() appelle dans un premier temps les
méthodes de la classe XmiAnalysator pour l’analyse d’un fichier XMI. Ensuite
createTable() lance l’analyse des meta-données obtenues en utilisant la classe
ScriptGenerator.
22.06.2005
6/22
SQL_DDLCreator
(f ro m hegn e)
SQL_DDLCreator()
SQL_DDLCreator()
createTable()
isAlreadyCreated()
ch.hegne.XmiAnalysator
Cette classe parse un fichier XMI en utilisant l’API JAXP (méthode DOM) et stocke les
meta-données dans un container de type XmiData. La méthode getClass() fait appel à la
méthode getAttributes() qui recherche les attributs d’une classe. Il faut préciser que la
méthode getClass() doit impérativement être appelée avant getAssociations(). En effet,
cette dernière recherche les classes précédemment trouvées pour ensuite y ajouter les
associations détectées.
La recherche d’informations dans un fichier XMI fait appel à des processus récursifs. Ces
derniers doivent souvent recommencer l'analyse du fichier depuis la racine ce qui rend
assez complexe la mémorisation des données trouvées. Nous avons donc choisi
d'implémenter un parseur travaillant en plusieurs passes. Pour ce faire nous avons choisi
d'utiliser des attributs de classes (static) afin d'assurer le partage temporaire des
données entre les différents processus récursifs.
ch.hegne.ScriptGenerator
Cette classe génère le script SQL-DDL. Elle procède en deux phases. La
consiste à convertir les données reçues par la classe XmiAnalysator en
relationnelles.
Cette
conversion
s’effectue
grâce
à
la
generateRelationalDataXmi(). Cette dernière réalise simplement le mapping
types des attributs utilisés dans le modèle UML et le langage SQL.
première
données
méthode
entre les
Pour le mapping UML – SQL des types de données, nous nous sommes basés sur le
mapping proposé par Rational Rose.
22.06.2005
XMI:
SQL:
STRING
VARCHAR2 (255)
INTEGER
NUMBER (10,0)
DOUBLE
FLOAT
DATE
DATE
BOOLEAN
NUMBER (1,0)
BYTE
NUMBER (3,0)
SINGLE
FLOAT
LONG
NUMBER (20,0)
CURRENCY
FLOAT
7/22
Il faut préciser que les méta-données de type RelationalData ne peuvent pas servir
directement à la création du script SQL-DDL, elles doivent être encore analysées pour
éviter par exemple le problème du double sens de navigabilité ou gérer les associations
de type n-n. La deuxième phase qui consiste donc à analyser ces méta-données et est
réalisée par la méthode analyseRelationalData(). Celle-ci génère directement le script
SQL-DDL par le biais de la méthode writeScript().
ch.hegne.XmiData
La classe XmiData sert à stocker les méta-données trouvées dans le fichier XMI analysé.
Elle comporte un champ mémorisant le nom du modèle et un tableau contenant des
objets de type XmiClass.
La classe XmiClass contient un tableau d’objets de type XmiAttribut et un autre tableau
stockant des objets de type XmiAssociation.
22.06.2005
8/22
XmiData
(from hegne)
Modelname : Logical View::java::lang::String = ""
0..*
XmiConstraint
(from hegne)
name : Logical View::java::lang::String = ""
expression : Logical View::java::lang::String = ""
extendedElement : Logical View::java::lang::String = ""
isCheckType : boolean = true
0.. *
XmiClass
(f rom heg ne)
id : Logical View::java::lang::String = ""
name : Logical View::java::lang::String = ""
0..*
XmiAssociation
(f ro m he gne)
name : Logical View::java::lang::String = ""
refclass : Logical View: :java::lang::St ring = ""
rolename : Logical View: :java::lang::St ring = ""
uppermultiplicity : Logical View::java: :lang::String = ""
lowermult iplicity : Logical View: :java::lang::St ring = ""
XmiAtt ribut
(from hegne)
id : Logical View::java::lang::String = ""
name : Logical View::java::lang::String = ""
type : Logical View::java::lang::String = ""
lowerMultiplicity : Logical View::java::lang::String = "0"
isAtrributAssociation : boolean = false
0..*
ch.hegne.RelationalData
Cette classe est un conteneur permettant de stocker les méta-données contenues dans le
fichier XMI sous forme relationnelle. Cette classe comporte un tableau d’objets de type
Table. La classe Table comporte un tableau d’objets de type Attribut. Les champ
isComposed (si l’attribut est composé), isTable (si l’attribut est un tableau), isTraited (si
l’attribut a déjà été analysé) et isReferenced (qui permet d’éviter le problème du double
sens de navigation) facilitent l’analyse finale effectuée par la méthode
analyseRelationalData() de la classe ScripGenerator.
22.06.2005
9/22
RelationalData
(f rom heg ne)
size : int = 0
0.. *
Table
(f rom heg ne)
size : int = 0
name : Logical View::java::lang::String
0.. *
At tribut
(f rom heg ne)
name : Logical View::java::lang::String = ""
type : Logical View::java::lang::String = ""
xmiid : Logical View::java::lang::String = ""
isComposed : boolean = false
isTable : boolean = false
isTraited : boolean = false
isReferenced : boolean = false
isAtrributAssociation : boolean = false
0.. *
Constraint
(f rom heg ne)
name : Logical View::java::lang::String = ""
expression : Logical View::java::lang::String = ""
1.6.3. Fichier de configuration
Un fichier de configuration s’appelant conf.txt doit être placé à la racine du disque C.
Exemple de configuration :
PATHLOGFILE=c:\\Daucourt\\log\\sql_ddlCreator.log
SQL_USERNAME=Daucourt
SQL_PASSWORD=Daucourt
VARCHAR_SIZE=255
PATHLOGFILE
SQL_USERNAME
SQL_PASSWORD
VARCHAR_SIZE
22.06.2005
Fichier de journalisation des événements de l’analyse XMI
Nom de l’utilisateur pour se connecter à la BD (pas utilisé)
Mot de passe de l’utilisateur pour se connecter à la BD (pas utilisé)
Entier utilisé pour définir la taille des chaînes de caractères en SQL
10/22
1.6.4. Interface graphique
Au chargement de l’application, le bouton permettant l’ouverture et par conséquent
l’analyse d’un fichier XMI est désactivé. Il faut d’abord choisir le répertoire de destination
(save directory) avant de pouvoir sélectionner un fichier XMI.
L’onglet Events affiche le résultat de l’analyse du fichier XMI. Un fichier de log contenant
ces informations est également (voir la propriété PATHLOGFILE du fichier de
configuration).
22.06.2005
11/22
L’onglet Created tables présente quant à lui les différentes commandes SQL-DDL qui ont
été générées suite à l’analyse du fichier XMI. On retrouve le code généré pour chaque
table dans un fichier texte dans le répertoire de destination (save directory). Le fichier
est préfixé avec sql_ et est suivi du nom de la classe et de l’extension .txt.
22.06.2005
12/22
1.7. Application de démonstration
Voici le modèle UML que nous avons choisi pour illustrer l'utilisation de notre prototype
de générateur SQL-DDL:
22.06.2005
13/22
Eleve
<<PK>> Numero : Integer
Nom : String
Prenom : String
age : Integer
Eleve.age > 0
Inscription
+eleve
+eleve
Dat eIns cription : Date
0..*
0..*
estInscrit
+filiere
appartient
0..*
1
Filiere
+classe
Class e
<<PK>> Numero : Integer
Libelle : String
<<PK>> Numero : Integer
Libelle : String
Ce modèle contient tout les éléments intéressants disponibles en UML:
Association 1-N
Association N-N
Classe-association avec attribut
Contrainte stéréotypée (PK, NOT NULL)
Multiplicité minimale à un
Contrainte OCL (CHECK)
•
•
•
•
•
•
Remarque 1: Le nom des rôles est obligatoire car il est utilisé pour générer les attributs
matérialisant une clé étrangère.
Remarque 2: La contrainte OCL a été rajoutée à la main au fichier XMI généré par
Rational Rose car cet outil ne supporte pas l'édition de contraintes OCL pour les attributs.
Nous nous sommes inspirés pour cela du code XMI généré par Poseidon for UML. Nous
avons bien essayé d'utiliser Poseidon for UML pour générer automatiquement l'ensemble
du fichier XMI mais nous avons constaté que les deux outils n'utilisaient pas la même
version de XMI (1.1 pour Rational Rose, 1.2 pour Poseidon for UML) et que cela posait
des problèmes pour l'analyse du fichier.
Voici le code SQL-DDL généré par notre prototype pour ce modèle:
CREATE TABLE Classe(
Numero
Libelle
);
NUMBER(10,0)
VARCHAR2(255)
PRIMARY KEY,
CREATE TABLE Eleve(
Numero
NUMBER(10,0)
PRIMARY KEY,
Nom
VARCHAR2(255)
NOT NULL,
Prenom
VARCHAR2(255)
NOT NULL,
age
NUMBER(10,0)
check (age > 0),
num_classe
INTEGER REFERENCES
Classe(Numero)
);
CREATE TABLE Filiere(
Numero
Libelle
22.06.2005
NUMBER(10,0)
VARCHAR2(255)
14/22
PRIMARY KEY,
);
CREATE TABLE Inscription(
num_filiere
INTEGER REFERENCES
num_eleve
INTEGER REFERENCES
DateInscription
DATE
);
Filiere(Numero),
Eleve(Numero),
Avant de lancer ce code SQL dans la base de données (copier/coller avec SQL Plus par
exemple), il faut réfléchir à l’ordre dans lequel il faut exécuter les instructions afin de
respecter les contraintes d’intégrités référentielles.
Alternative au parseur, simplification du
prototype
1.8. Introduction
Lors de la réalisation du prototype, il nous est apparu qu’il serait intéressant d’utiliser les
fonctionnalités de la base de donnée Oracle pour en simplifier le code. En effet, la base
de données cible retenue étant forcément une base de donnée de ce constructeur, nous
avons exploré les fonctionnalités de traitement de données XML offert. Le concept de
base consiste à charger le fichier XMI dans la base de données et d’utiliser les outils
basés sur Xpath pour simplifier la recherche de la valeur des nœuds.
Nous n’avons pas modifié le prototype par manque de temps, mais réaliser quelques
manipulations simples, soit :
•
•
•
•
Installation de Oracle XMLDB
Création d’une structure capable de stocker le document XMI
Chargement du fichier dans la base de données
Recherche et extraction de données sur des critères Xpath.
1.9. Exemple de manipulation
Les tests ont été fait à partir d’une extraction du fichier XMI exemple, avec une seule
classe, la classe Client
Client
numero : int
nom : string
Une fois la classe saisie avec Poseidon, nous avons généré le fichier XMI correspondant,
dont voici un extrait :
<?xml version = '1.0' encoding = 'UTF-8' ?>
<XMI xmi.version = '1.2' xmlns:UML = 'org.omg.xmi.namespace.UML'
timestamp = 'Mon Jun 16 15:46:04 CEST 2003'>
<XMI.header>
<XMI.documentation>
<XMI.exporter>Netbeans XMI Writer</XMI.exporter>
<XMI.exporterVersion>1.0</XMI.exporterVersion>
</XMI.documentation>
</XMI.header>
22.06.2005
15/22
t
<XMI.content>
<UML:TagDefinition xmi.id = 'a1' name = 'element.uuid'
isSpecification = 'false'
tagType = 'element.uuid'>
<UML:TagDefinition.multiplicity>
<UML:Multiplicity xmi.id = 'a2'>
<UML:Multiplicity.range>
<UML:MultiplicityRange xmi.id = 'a3' lower = '1' upper = '1'/>
</UML:Multiplicity.range>
</UML:Multiplicity>
</UML:TagDefinition.multiplicity>
</UML:TagDefinition>
<UML:Model xmi.id = 'a4' name = 'Demo XMI'
isSpecification = 'false' isRoot = 'false' isLeaf = 'false'
isAbstract = 'false'>
<UML:ModelElement.taggedValue>
<UML:TaggedValue xmi.id = 'a5' isSpecification = 'false'
dataValue = '-99-26--92-79-247d4a:f5d0b19cde:-8000'>
<UML:TaggedValue.type>
<UML:TagDefinition xmi.idref = 'a1'/>
</UML:TaggedValue.type>
</UML:TaggedValue>
</UML:ModelElement.taggedValue>
<UML:Namespace.ownedElement>
<UML:Class xmi.id = 'a6' name = 'Client' visibility = 'public'
isSpecification = 'false' isRoot = 'false' isLeaf = 'false'
...
1.9.1. Création de la table
La première étape consisite à créer une structur capable de stocker le document XMI
dans la base de données. Il existe plusieurs manière de réaliser ce stockage, nous avons
opté pour le cas le plus simple, une table relationnelle objet basé sur la classe XMLType.
create table xmi_table of xmltype;
1.9.2. Chargement du document XMI dans la base
de données
Le document doit d'abord être converti en une instance XMLType. Ceci est réalisé grâce
aux constructeurs fournis par le type XMLType..
INSERT INTO xmi_table
VALUES(XMLTYPE('<code_du_document_xmi>'));
SQLPlus n'acceptant qu'un nombre de caractères limité, il n'est pas possible de le charger
de cette manière.
Deux alternatives existent : SQLLoader ou une fonction PL/SQL getDocument(). Cette
dernière est décrite ci-après.
1.9.2.1. Fonction getDocumetn()
22.06.2005
16/22
Création d'une fonction getDocument() qui instancie un document XML en XMLType, à
laquelle il suffit de spécifier le nom du fichier à charger, pour autant que le document soit
dans un répertoire spécifique sur le serveur.
L'idée est de créer un dossier dans la base Oracle pointant sur un répertoire du serveur
contenant les documents XML à charger. La fonction retourne une variable CLOB
contenant le document XML.
Pour créer le répertoire il faut être connecté comme system
create directory XMLDIR as 'c:\TEMP\xml';
grant read on directory xmldir to public
with grant option;
En fait de répertoire, Oracle fait pointer le dossier XMLDIR sur un emplacement existant
du serveur, ici c:\TEMP\xml, qui est le dossier dans lequel sont contenus les fichiers XML
à charger dans la base oracle.
Créer une fonction qui retourne une valeur CLOB d'un fichier XML dont le nom est passé
en paramètres.
create function getDocument(filename varchar2)
return clob
authid current_user is
xbfile bfile;
xclob clob;
begin
xbfile := bfilename('XMLDIR',
filename);
dbms_lob.open(xbfile);
dbms_lob.createtemporary(xclob,
TRUE,
dbms_lob.session);
dbms_lob.loadfromfile(xclob,
xbfile,
dbms_lob.getlength(xbfile));
dbms_lob.close(xbfile);
return xclob;
end;
/
1.9.2.2. Insertion d'un document dans la base
A présent un document XML peut être chargé dans la table à l'aide de la fonction créée
en spécifiant uniquement le nom du fichier en paramètres. Le fichier à donc été placé au
préalable à l'endroit adéquat sur le serveur. Il suffit de remplacer le contenu du
document XMI dans la syntaxe d'insertion, par l'appel de cette fonction :
INSERT INTO xmi_table VALUES
(XMLTYPE(getDocument('fichierXMI.xml')));
1.9.3. Requêtes sur un document de la base
Nous testons ici quelques manipulations de bases pour extraire finalement la valeur des
nœuds en connaissant le chemin Xpath.
22.06.2005
17/22
1.9.3.1. Requête pour afficher le document en entier
Deux syntaxes différentes sont supportées pour le même résultat :
select x.getclobval()
from xmi_table x;
select value(x)
from xmi_table x;
1.9.3.2. ExistsNode()
Cette fonction renvoie la valeur booléenne VRAI (1) si le chemin Xpath saisi correspond à
un nœud existant dans le document, ou FAUX (0) si elle ne le trouve pas.
Par exemple pour savoir si le nœud '/XMI/XMI.header/ XMI.documentation ' existe
comme dans l'extrait ci-dessous :
<?xml version = '1.0' encoding = 'UTF-8' ?>
<XMI xmi.version = '1.2' xmlns:UML = 'org.omg.xmi.namespace.UML'
timestamp = 'Mon Jun 16 15:46:04 CEST 2003'>
<XMI.header>
<XMI.documentation>
<XMI.exporter>Netbeans XMI Writer</XMI.exporter>
<XMI.exporterVersion>1.0</XMI.exporterVersion>
</XMI.documentation>
</XMI.header>
il suffit d'exécuter la commande suivante :
SELECT existsNode(x.data,
'/XMI/XMI.header/XMI.documentation')
as doc
FROM xmi_table x;
DOC
---------1
Si le nœud n'existe pas :
SELECT existsNode(x.data,
'/n_importe_quoi/')
as doc
FROM xmi_table x;
DOC
---------0
S'il y a deux documents dans la table contenant un tel nœud:
SELECT existsNode(x.data,
'/XMI/XMI.header/XMI.documentation')
as doc
FROM xmi_table x;
DOC
22.06.2005
18/22
---------1
1
Pour accéder à un nœud explicitement au cas ou deux nœud avec la même hiérarchie
existeraient dans le même document, il suffit d'ajouter un chiffre entre crochets, "[]",
indiquant la position du nœud par rapport aux autres ayant la même hiérarchie (en
partant de 1). Don comme exemple pour le premier :
SELECT existsNode(value(x),
'/XMI/XMI.header/XMI.documentation[1]')
as doc
FROM xmi_table x;
DOC
---------1
1.9.3.3. Afficher un noeud
L'affichage d'un tag et de son contenu se fait de la même manière qu'auparavant :
SELECT
extract(value(x),
'/XMI/XMI.header/XMI.documentation/XMI.exporter').getclobval()
as exporter
FROM xmi_table x;
EXPORTER
--------------------------------------------------------------<XMI.exporter>Netbeans XMI Writer</XMI.exporter>
1.9.3.4. Contenu d'un tag
Pour extraire une information d'un tag (entre la balise d'ouverture et celle de fermeture),
il faut utiliser extract() et getStringval().
SELECT
extract(value(x),
'/XMI/XMI.header/XMI.documentation/XMI.exporter/text()').
getstringval() as Exporter
FROM xmi_table x;
EXPORTER
----------------------Netbeans XMI Writer
Il est possible de raccourcir la syntaxe en utilisant les capacités de Xpath
ainsi:
SELECT extract(value(x),
'//XMI.exporter/text()').getstringval()
as exporter
FROM xmi_table x;
22.06.2005
19/22
ExtractValue() peut aussi être utilisé :
SELECT extractValue(value(x), '//XMI.exporter')
as exporter
FROM xmi_table x;
EXPORTER
----------------------Netbeans XMI Writer
Pour afficher plusieurs nœuds il suffit de remonter dans la hiérarchie (le chemin indiqué
sera plus court) :
SELECT extract(value(x),
'/XMI/XMI.header/XMI.documentation').getstringval()
as Documentation
FROM xmi_table x;
DOCUMENTATION
------------------------------------------------------------<XMI.documentation>
<XMI.exporter>Netbeans XMI Writer</XMI.exporter>
<XMI.exporterVersion>1.0</XMI.exporterVersion>
</XMI.documentation>
1.9.4. Afficher les enfants d'un nœud
SELECT extract(value(x),
'/XMI/XMI.header/XMI.documentation/*') as doc
FROM xmi_table x;
DOC
----------------------------------------------------<XMI.exporter>Netbeans XMI Writer</XMI.exporter>
<XMI.exporterVersion>1.0</XMI.exporterVersion>
Pour n'afficher que le contenu des enfants d'un nœud il n'est pas possible d'utiliser
extractvalue() car cette fonction ne doit retourner qu'un seul nœud:
Une possibilité existe avec extract(), mais les informations extraites sont concaténées en
une seule chaîne, ce qui n'est pas vraiment idéal :
SELECT extract(value(x),
'/XMI/XMI.header/XMI.documentation/*/text()') as doc
FROM xmi_table x;
DOC
---------------------------------Netbeans XMI Writer1.0
Il est plus intéressant s’utiliser la séquence XML (XMLSequence) avec une table:
SELECT value(t)
FROM xmi_table x,
22.06.2005
20/22
TABLE (xmlsequence(extract(value(x),
'/XMI/XMI.header/XMI.documentation/*'))) t;
VALUE(T)
-----------------------------------------------------<XMI.exporter>Netbeans XMI Writer</XMI.exporter>
<XMI.exporterVersion>1.0</XMI.exporterVersion>
et avec extractvalue() pour n'obtenir que le contenu des tags.
SELECT extractvalue(value(t), '/')
FROM xmi_table x,
TABLE (xmlsequence(extract(value(x),
'/XMI/XMI.header/XMI.documentation/*'))) t;
EXTRACTVALUE(VALUE(T),'/')
------------------------------------------Netbeans XMI Writer
1.0
1.9.4.1. Attribut d'un tag
Pour obtenir le nom de la Classe qui se trouve comme attribut d'un tag, il faut utiliser
@nom_de_l'attribut à la place de text(), en plus des fonctions extract() et getStringval().
Avec le raccourci Xpath, on obtient :
SELECT x.extract('//UML:Class/@name').getstringval() as classe
FROM xmi_table x;
CLASSE
-----------Client
Pour la reprise des attributs, les résultats de cette syntaxe n'est malheureusement pas
satisfaisante, s'il existe plusieurs informations avec le même chemin. ces dernières sont
alors concaténées dans une chaîne de caractères.
Par exemple pour le nom des attributs:
SELECT x.extract('//UML:Attribute/@name') AS Attribute
FROM xmi_table x;
ATTRIBUTE
------------numeronom
Pour obtenir ces informations séparément il faudrait donc utiliser une table alimentée par
une séquence XML comme vu précédemment.
1.10. Conclusion
L’utilisation des capacités d’Oracle XMLDB d’Oracle serait assez intéressant pour
simplifier le code du prototype pour éviter l’utilisation du parseur, en s’appuyant en
22.06.2005
21/22
particulier sur les requêtes Xpath. La maintenance du prototype serait nettement
simplifiée, particulièrement en cas de modification/évolution de la norme XMI. Nous
n’avons pas réalisé faute de temps, un prototype avec ces fonctionnalités, mais en
première approche, cela ne semble guère compliqué, puisque la connexion peut se faire
avec des drivers JDBC qui sont réputés très stables.
Nous sommes néanmoins plus nuancé quand à la mise en pratique réelle de cette
solution. Nous avons du hélas constaté que les fonctionnalités XMLDB d’Oracle 9.2 ne
sont pas encore complètement mature, et nous avons du composer avec un produit des
plus instable. Une problématique non traitée consiste à traiter le retour des requêtes qui
sont parfois complexes et encore structurées. Un exemple simple peut être illustré par
l’apparition d’un apostrophe dans une chaîne de caractère résultat.
22.06.2005
22/22
Téléchargement