|
Accueil
Les tutoriels de Sashipa
Pour apprendre le langage Sashipa, vous pouvez commencer par :
Par la suite, ce sont les HOWTO
qui vous seront utiles, en documentation de référence.
Tutoriel avec approche théorique
Introduction
Sashipa est un format XML pour décrire des applications interfaces
de bases de données relationnelles. Je donne ici quelques
précisions sur les bases de données avec lesquelles
Sashipa-Melba peut travailler.
Voici un tutoriel pour écrire vos premières applications
Sashipa. Il repose sur l'exemple de la base DemoContact. Si vous ne l'avez
pas déjà installée,
cliquez ici.
Bases de données relationnelles
Une base de données relationnelle est un ensemble de tables
composées chacunes de colonnes. Les données sont les
lignes. On les appelle aussi enregistrements ou tuples.
Une base est créée, puis interrogée, au moyen d'un
langage standard : le SQL.
Voici le script SQL de création de la base des Contacts :
CREATE TABLE PROFESSION (
ProfessionId integer NOT NULL PRIMARY KEY,
ProfessionLabel varchar(100) NOT NULL UNIQUE
);
CREATE TABLE CONTACT (
ContactId integer NOT NULL PRIMARY KEY,
ContactNom varchar(100) NOT NULL,
ContactPrenom varchar(100),
TelPortable varchar(20),
Email1 varchar(100),
Email2 varchar(100),
Commentaire text,
ProfessionRef integer NOT NULL REFERENCES PROFESSION(ProfessionId),
FonctionLabel varchar(100),
UNIQUE (ContactNom, ContactPrenom)
);
CREATE TABLE CONTACTADRESSE (
ContactAdresseId integer NOT NULL PRIMARY KEY,
ContactAdresseLabel varchar(100) NOT NULL,
IsProfessionnelle integer NOT NULL,
AdresseRue varchar(255),
AdresseCP char(5),
AdresseVille varchar(255),
Tel1 varchar(20),
Tel2 varchar(20),
Fax varchar(20),
Email varchar(100),
ContactRef integer NOT NULL REFERENCES CONTACT(ContactId),
UNIQUE (ContactRef, ContactAdresseLabel)
); |
Identifiants, les bonnes manières
Chaque table dispose d'une clef primaire, le plus souvent
composée d'une unique colonne de type integer (exemple :
ProfessionId pour la table PROFESSION). Cette colonne servira exclusivement
à identifier les lignes de façon unique au sein de la base.
Les données mises dans cette colonne ne devront jamais être
modifiées par la suite sous peine de rendre la base non
intègre.
En règle générale, les valeurs d'une clef primaire ne sont jamais
affichées à l'utilisateur. En effet, si c'était le cas, l'utilisateur les utiliserait pour ses
propres besoin (pour ses dossiers papier par exemple) et tôt ou tard aurait besoin de les modifier.
L'utilisateur a besoin, lui aussi, d'identifier chaque ligne de la table. On utilise pour cela la contrainte UNIQUE
qui fonctionne comme une clef primaire, sauf qu'elle ne permet pas de faire de liens entre les tables (exemple:
ProfessionLabel pour la table PROFESSION).
Pour chaque table, on peut donc distinguer l'identifiant "base de données" (la clef primaire)
de l'identifiant "utilisateur" (la contrainte UNIQUE).
Les liens entre les tables sont faits au moyen de clefs étrangères
référençant les clefs primaires. Exemple: ProfessionRef dans la table CONTACT référence
la colonne ProfessionId de la table PROFESSION. Ceci implique que les valeurs que l'on trouve dans CONTACT.ProfessionRef
pointent sur les valeurs de PROFESSION.ProfessionId. De cette manière, on peut retrouver la profession de chaque
contact.
Pour une explication détaillée, voir l'article
Clefs et base de données, les bonnes manières.
Fonctionalités du SGBD à éviter !
Attention : concernant les clefs primaires auto-incrémentées par le SGBD, cette fonctionnalité n'est pas supportée
pour tous les SGBD. Mais vous pouvez remédier facilement à ça, cf la
HOWTO. Vous pouvez aussi déclarer une clef
primaire comme un simple 'integer' et une valeur sera automatiquement générée par l'application lors d'un ajout.
Deuxième problème : les applications générées disposent d'un cache pour mémoriser les derniers enregistrements chargés.
Ce cache est rafraichi lorsque la partie serveur (la servlet) signale une modification faite par une de ses applications
clientes. Donc vous aurez des problèmes de cache non à jour si vous faites des
modifications par triggers ou si un programme tiers accède à la base sans passer par la servlet.
Vous trouverez le source
ici.
Voici pour commencer le squelette de l'application DemoContact en Sashipa.
<?xml version='1.0' encoding='ISO-8859-1' ?>
<!DOCTYPE application SYSTEM 'resources/sashipa.dtd' [
<!ENTITY br '
'>
<!ENTITY nbsp ' '>
<!ENTITY frenchDefinition SYSTEM 'resources/SashipaFrench.xml'>
<!ENTITY englishDefinition SYSTEM 'resources/SashipaEnglish.xml'>
]>
<application name='AppliContacts'>
<!--........ environment ........ -->
<environment>
<!-- ... description des SGBD et de leurs bases ... -->
</environment>
<!--........ graphicalUserInterface ........ -->
<graphicalUserInterface name='guiContact'>
<!-- ... description de l'interface utilisateur ... -->
</graphicalUserInterface>
<!--........ servers ........ -->
<serverSet guiType='application'>
<!-- ... description of the Sashipa servers and connections
to DBMS ... -->
</serverSet>
<!--........ architecture ........ -->
<architecture>
<!-- ... description of deployment and used language ... -->
</architecture>
</application> |
Les principales parties de ce fichier XML sont :
-
La description de la structure de l'environement serveur et
de sa base de données DemoContact
- La description de l'interface utilisateur (GUI pour Graphical User Interface)
- La description des serveurs.
- La description de déploiementde votre application.
La première partie d'un fichier Sashipa est la description
de l'environnement serveur.
Le nom réel du serveur est stocké dans <physicalName>.
Remarque : le nom 'dbmsMain' est interne au fichier Sashipa et sert quand
des éléments du fichier font référence à
ce serveur. Notez que c'est un point commun à chaque élément
dans Sashipa : l'attribut "name" d'un composant sert uniquement à
l'identification de celui-ci au sein du fichier Sashipa et ne correspond pas au
nom réel.
<environment>
<dbmsSet>
<dbms name='dbmsMain'>
<physicalName>localhost</physicalName>
<databaseSet>
<!-- ... description des bases de données ... -->
</databaseSet>
</dbms>
</dbmsSet>
</environment> |
C'est ici, notamment, qu'est décrite la base de données
DemoContact. Je donne ci-dessous la description de la table
"PROFESSION" ainsi que de la colonne CONTACT.ProfessionRef
pour un exemple de clef-étrangère.
<database name='dbContact'>
<physicalName>DemoContact</physicalName>
<singularName>Base des Contacts</singularName>
<schemaTableSet>
<!-- ............. PROFESSION ............. -->
<schemaTable name='tableProfession'>
<physicalName>PROFESSION</physicalName>
<singularName>Profession</singularName>
<pluralName>Profession(s)</pluralName>
<schemaColumnSet>
<schemaColumn name='pro_ProfessionId' type='integer'
notNull='yes' pk='yes'>
<physicalName>ProfessionId</physicalName>
<singularName>Identity</singularName>
</schemaColumn>
<schemaColumn name='pro_ProfessionLabel' type='text' notNull='yes'
maxCharacters='100'>
<physicalName>ProfessionLabel</physicalName>
<singularName>Profession</singularName>
<guiConfigSchemaColumn letterCount='20' sort='asc' />
</schemaColumn>
</schemaColumnSet>
<userKey>
<userKeyColumn schemaColumn='pro_ProfessionLabel' />
</userKey>
</schemaTable>
<!-- ............. CONTACT ............. -->
<schemaTable name='tableContact'>
<schemaColumnSet>
...
<schemaColumn name='cta_ProfessionRef'
type='integer' notNull='yes'>
<physicalName>ProfessionRef</physicalName>
<singularName>Profession</singularName>
</schemaColumn>
...
</schemaColumnSet>
<schemaFkSet>
<schemaFk name='fk_cta_Profession'
targetSchemaTable='tableProfession'>
<schemaColumnRef schemaColumn='cta_ProfessionRef' />
</schemaFk>
</schemaFkSet>
</schemaTable>
</schemaTableSet>
</database> |
Structure générale
Voici la structure générale d'une GUI Sashipa.
<graphicalUserInterface name='guiContact'>
<resourceName>Contact</resourceName>
<mainTitle>[Contact]</mainTitle>
<separatorTitle> - </separatorTitle>
<size w='800' h='600' />
<guiStarting>
<loadingScreen>
<mainScreenRef screen='SMMain' />
</guiStarting>
<screenSet>
<!-- ... liste des Screens ... -->
</screenSet>
<formSet>
<!-- ... liste des Forms ... -->
</formSet>
</graphicalUserInterface> |
Important : le bloc <resourceName> contient le nom de
la classe principale de l'application. La commande de lancement de
votre application généré sera donc "java
Contact" dans notre cas.
Le bloc <mainScreenRef> précise le premier écran
qui sera affiché lors du lancement de l'application.
Les composants de la GUI
L'apparence de l'interface d'une base de données est fortement
liée au schéma (la structure) de sa base de données.
Sashipa distingue trois niveaux dans les composants de cette interface :
les Screens, les Forms et les Fields.
Je ne donne pas d'exemple de code XML pour les composants de la GUI. Je
recommande au lecteur de se référer au code source pour
visualiser concrètement ce qu'il lit.
Les Screens
Une application interface de base de données affiche une succession
d' écrans : ce sont les Screens. L'application n'affichera
qu'un unique Screen à la fois.
Les Forms
Un Screen est un conteneur de formulaires. Pour chaque table, on aura
les formulaires suivant :
- Une fiche de consultation / modification qui travaille ligne par ligne dans la table. C'est le CardForm.
- Une liste pour afficher un sous-ensemble ou la totalité de la table. C'est le ListForm.
- Un formulaire de recherche pour retrouver des enregistrements dans la table. C'est le ResearchForm et il utilise lui-même le ListForm pour afficher les résultats de la recherche.
- Dans le cas des tables comportant beaucoup d'enregistrements qu'il faut référencer : un formulaire pour sélectionner l'enregistrement. C'est le SelectRecordForm. Celui-ci se base sur le ResearchForm.
De plus, on crée les menus avec les MenuForm. Les menus sont
indépendants des tables. Un menu permet d'ouvrir d'autres Screens.
Les Fields et FkFields
Les formulaires CardForm et ResearchForm contiennent un ensemble de champs. Il existe deux catégories de champs :
- Les champs simples (Fields)
affichant la valeur d'une colonne de la table du formulaire.
- Les champs de clefs étrangères
(FkFields) permettant à l'utilisateur de sélectionner
une ligne de la table liée référencée par la clef
étrangère.
Voici la liste des Fields disponibles :
- Le champ qui affiche une valeur
sous forme de texte. C'est le champ textField.
- Le champ qui affiche une valeur
textuelle sur plusieurs lignes. C'est le champ textAreaField.
- Le champ pour afficher une valeur
booléenne. C'est le champ checkBoxField.
Et la liste des FkFields :
- Le champ affichant la liste des
lignes sélectionnables. C'est le champ comboBoxFkField. L'équivalent
du Combo-box.
- Le champ affichant la liste des
lignes sélectionnables et permettant en outre de saisir une valeur
existante. C'est le champ writeChoiceFkField.
- Le champ affichant la ligne référencée
sans permettre de la modifier. C'est le readOnlyTextFkField. Ce champ est
à utiliser en association avec un SelectRecordForm. C'est la
solution lorsque la liste des enregistrement à choisir est trop importante.
Conception en Sashipa
Voici quelques notions théoriques sur comment déduire
une interface graphique d'un schéma de base de données.
Incidence des clefs étrangères sur l'interface graphique
Prenons comme exemple les tables CONTACT et CONTACTADRESSE. La table
CONTACTADRESSE possède une clef étrangère
composée d'une unique colonne ContactRef référençant
la colonne CONTACT.ContactId .
Une interface classique pour cette base sera :
Screen 1 : Un menu (MenuForm) avec entre-autres un bouton
menant au Screen 2.
Screen 2 : Un ListForm contenant la totalité des
lignes présentes dans la table CONTACT. Si l'on double-clique sur une
des lignes, on ouvre le Screen 3.
Screen 3 :
-
Un CardForm pour afficher la fiche
du contact
-
Un ListForm pour afficher la liste
des adresses de ce contact. Si l'on double-clique sur une des lignes, on ouvre
le Screen 4.
Screen 4 : Un CardForm pour afficher la fiche de l'adresse (une ligne de CONTACTADRESSE). De plus, dans ce CardForm, on aura un FkField pour afficher / choisir le contact auquel cette adresse est liée.
Plus généralement, une clef-étrangère implique :
-
Pour la table cible, dans le Screen de son CardForm, une liste des enregistrements
dans la table dépendante.
-
Pour la table dépendante, un FkField dans sa CardForm.
Notion de filtres
Quand l'application ouvre un Screen, elle lui propose une
liste ordonnée de filtres ainsi que le filtre courant. Un
filtre est, au sens Sashipa du terme, la valeur d'une clef primaire.
Le Screen gère ensuite le passage d'un filtre à l'autre
dans la liste ordonnée avec les boutons Précédents
et Suivants.
Lorsque le Screen dispose d'un nouveau filtre, il le signale à
ses formulaires. Ceux-ci réagissent de façon
différente :
-
Le CardForm attend un filtre clef-primaire de sa table. C'est
en fait l'identifiant de la ligne dont il va afficher le contenu dans
ses champs. Si le filtre est absent, la fiche se met en mode 'Ajout'.
Si le filtre est présent mais ne correspond pas à la clef
primaire de sa table, une erreur est générée. De
plus le filtre est transféré aux FkFields.
-
Le ListForm attend une valeur de clef-primaire
référencée par l'une des clefs-étrangères
de ses tables. Ou bien une valeur de l'une des clefs-primaires de ses
tables. A défaut, le filtre est ignoré (donc la liste
n'est pas filtrée).
-
Le ResearchForm se contente de transmettre le filtre à sa
ListForm résultat et ses FkFields. Ceci induit que un ResearchForm
filtré effectue sa recherche dans un sous-ensemble de la table.
-
Le SelectRecordForm transmet le filtre au ResearchForm sur lequel il
est basé. Donc même remarque.
-
Le MenuForm passe le filtre comme paramètre d'ouverture au
Screen que l'utilisateur choisie d'ouvrir.
Les champs FkFields sont aussi sensibles aux filtres, de la même
manière qu'un ListForm.
Remarque: ces comportements par défaut peuvent être
paramétrés au moyen de CastFilter.
Les informations sur les servlets et les connexions aux bases de données
utilisées sont décrites ici.
<serverSet>
<server name='srvDemoContact' type='servlet'>
<dbConnection database='dbDemoContact' type='odbc' dbmsType='MySQL'>
<dbConnectionString>
DRIVER=MySQL;HOST=localhost;DB=DemoContact
</dbConnectionString>
<user>root</user>
<password></password>
</dbConnection>
<configStorage>
<configDbStorage database='dbDemoContact' sashipaConfigStorage='cfgMain' />
</configStorage>
<logStorage>
<mainLog><logFileStorage>
<uri>Melba_DemoContact_Main.log</uri>
</logFileStorage></mainLog>
<updateLog><logFileStorage>
<uri>Melba_DemoContact_Update.log</uri>
</logFileStorage></updateLog>
</logStorage>
<specificToServlet>
<servletResourceName>DemoContactServlet</servletResourceName>
<servletUrl>
http://localhost:8080/servlet/DemoContactServlet
</servletUrl>
</specificToServlet>
</server>
</serverSet> |
L'attribut type='servlet' signifie que l'architecture de l'application
générée est client-servlet-base de données. Pour construire une
architecture client-base de données, il suffit de modifier ceci en
type='embeddedInGui'.
L'attribut database='dbDemoContact' fait référence à la base décrite
dans la partie Environment.
Note : le bloc <specificToServlet> n'est utilisé que pour une architecture avec
servlet. Sinon il est ignoré. On y trouve le nom de la classe de la servlet, ainsi que
l'url utilisée par la partie cliente pour accéder à la servlet.
Les fichiers journaux de votre application, gérés par la
couche d'accès au données, sont précisés ici aussi.
Toutes les informations spécifiques au déploiement des interfaces utilisateurs sont décrites
ici :
<architecture guiType='application'>
<guiInstance gui='guiDemoContact' type='application'>
<guiResourceName>GuiDemoContact</guiResourceName>
<usedConfigStorage server='srvDemoContact' />
<usedServerForDatabase server='srvDemoContact' database='dbDemoContact' />
</guiInstance>
<languageDefinitionSet mainLanguageDefinition='french'>
&frenchDefinition; &englishDefinition;
</languageDefinitionSet>
</architecture> |
On remarque l'attribut type='application' qui signifie qu'une application sera
générée. Pour générer une applet, il suffit de
le changer en type='applet'.
Enfin, vous choisissez la langue dans laquelle votre application travaillera.
J'espère que cette technologie vous sera utile.
Bon courage,
Thomas.
Glossaire
Base de données relationnelle |
Ensemble logique de tables interdépendantes. Elles sont stockées dans un SGBD/R. Elles sont interrogeables au moyen de requêtes SQL. |
GUI |
Graphical User Interface. Interface Utilisateur. |
SGBD/R |
Système de Gestion de Base de Données (/ Relationnel). Logiciel fonctionnant sur un serveur et stockant des bases de données. L'accès à ces bases se fait au moyen du langage SQL. |
SQL |
Structured Query Language. Langage d'interrogation de bases de données relationnelles. C'est un standard. |
Intégrité référentielle |
Dans une base de données relationnelle, les clefs étrangères permettent à une donnée de référencer une autre donnée. L'intégrité référentielle est le mécanisme qui assure que la donnée référencée existe bien. (par exemple que toutes les AdressePersonne référencent un identifiant de Personne existant) |
Clef étrangère |
Dans une base de données relationnelle, les clefs étrangères sont un ensemble de colonnes d'une table référençant la clef primaire d'une autre table (ou de la même). |
Clef primaire |
Dans une base de données relationnelle, la clef primaire d'une table est l'ensemble de colonnes de la table identifiant de manière unique chaque lignes. C'est à dire que pour chaque ligne insérée, les valeurs affectées aux colonnes de la clef primaire doivent être un ensemble unique. |
Top
|