PARTIE I : L`environnement d`Oracle

publicité
Licence Pro RTICPMO
Responsable des Technologies d’Information et de la Communication zn Petite et Moyennes Organisations
Bases de données
ORACLE
L’environnement d’Oracle
Développement d’applications clientes sous Developer 2000 / Forms Builder
Pierre-Jean Charrel et Jean-Marc Thévenin
Licence Pro RTICPMO, Université Toulouse 1, P. A. France, 31042 Toulouse Cedex,
Secrétariat : tél 05 61 63 35 11, email [email protected]
Sommaire
PARTIE I : L’environnement d’Oracle .................................................................................... 4
TP 1Connexion des Clients au Serveur Oracle, Pratique de SQL avec l'éditeur SQLWorksheet ............................................................................................................................................ 4
1. Objectifs ..........................................................................................................................................4
2. Environnement ...............................................................................................................................4
2.1. Les outils Oracle ..................................................................................................................................... 4
2.2. La base de données support des TP ........................................................................................................ 5
3. Actions ............................................................................................................................................6
3.1. A propos de l'outil SQL-Net8 Easy Config ............................................................................................. 6
3.2. Lancer l'outil Administration Toolbar ..................................................................................................... 7
3.3. Lancer l'outil SQL Worksheet ................................................................................................................. 7
3.4. Lancer l'outil Admin ODBC ................................................................................................................... 8
3.5. Lancer Microsoft ACCESS ..................................................................................................................... 8
3.6. Traiter les Requêtes SQL suivantes ........................................................................................................ 9
TP 2 Traduction de règles de gestion à l'aide de déclencheurs Bases de Données .............. 10
1. Objectifs ........................................................................................................................................10
2. Environnement .............................................................................................................................10
3. Actions ..........................................................................................................................................10
PARTIE II : Développement d’applications clientes avec Developper 2000 ........................ 14
TP 3 Le concept de formulaire dans Developer 2000 ............................................................ 14
1. Rappel : Les trois principaux objets d’un module ...................................................................14
Bloc (Block)................................................................................................................................................. 14
Elément (Item) ............................................................................................................................................. 15
Canevas (Canvas) ......................................................................................................................................... 15
2. Création d'un formulaire basique ..............................................................................................16
3. Création d’un formulaire comportant un sous formulaire .....................................................16
4. Exercice .........................................................................................................................................18
TP 4 Listes de valeurs (LOV) et déclencheurs d'applications. Création de boutons et de
déclencheurs associés à des LOV écrits en PL/SQL ........................................................................ 19
1 Rappel : la gestion des clés étrangères ........................................................................................19
2. création d’un formulaire exploitant une LOV ..........................................................................19
2.1. Exercice : expérimentation des LOV .................................................................................................... 19
2.2. Concepts autour des LOV ..................................................................................................................... 20
3. Améliorer l'utilisation d'une LOV .............................................................................................22
4. Déclencheurs d’application (triggers) ........................................................................................22
4.1. Exemple : contrôler l’affichage d’une LOV dans un formulaire .......................................................... 23
4.2. Déclencheurs d’application et déclencheurs base de données ............................................................... 23
5. Gestion automatique de l’affichage d’une LOV .......................................................................24
Solution 1 : création d’un déclencheur WHEN-MOUSE-DOUBLECLICK pour l’item NJ ....................... 24
Solution 2 : création d’un déclencheur PRE-TEXT-ITEM pour l’élément NJ ............................................ 24
Solution 3 : création d’un bouton avec un déclencheur WHEN-BUTTON-PRESSED ............................... 25
6. Gestion de LOV dynamiques ......................................................................................................25
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 2
TP 5 Synchronisations entre les Blocs de données (FORMS) et la Base de Données ......... 27
1. Notion de Bloc basé / non basé ...................................................................................................27
1.1. Remplissage du tableau d’un Bloc avec des données stockées dans la Base de données ...................... 27
1.2. Actualisation de la Base de données via le tableau d’un Bloc .............................................................. 27
2. Concevoir l’interface d’un Bloc basé .........................................................................................28
2.1. Utilisation d'alertes................................................................................................................................ 28
2.2. Contrôle du remplissage d’un Bloc ....................................................................................................... 29
2.3. Restriction dynamique du remplissage d’un Bloc ................................................................................. 30
2.4. Afficher des données calculées dans un Elément non basé ................................................................... 30
3. Corrélation entre les Blocs, l’actualisation de la Base de Données et les Déclencheurs
d’application ...............................................................................................................................................31
3.1 Création d’un bloc non basé comportant les boutons Valider et Annuler .............................................. 31
3.2. Gestion d’une contrainte d’intégrité au niveau de l’application ............................................................ 32
3.3. Création d’une contrainte sur un élément comparant les valeurs avant maj et après maj ...................... 33
3.4. Question de cours .................................................................................................................................. 34
TP 6 Gestion spécifique des blocs maître/détail lorsqu'il n'y a pas de jointure directe entre
les blocs. Gestion du remplissage des blocs détails à l'aide de déclencheurs écrits en PL/SQL .... 35
1 Position du problème ....................................................................................................................35
2. Utiliser la fonction Execut_Query dans un déclencheur déclencheur ON-POPULATEDETAILS ....................................................................................................................................................36
3. Création d’un bloc non basé affichant des données .................................................................37
4. Exercice : Ajout d'éléments calculés à ce formulaire ...............................................................39
TP 7 Compléments .................................................................................................................. 40
1. Les variables globales ..................................................................................................................40
2. Les variables système ..................................................................................................................40
3. Gestion de l’affichage de messages d’erreur .............................................................................41
4. Utilisation de séquences et des sous-programmes PL/SQL .....................................................42
4.1. Objectif des séquences : éviter les problèmes de saisie sur les clés. ..................................................... 42
4.2. Application : génération automatique du n° d'équipe ............................................................................ 42
5. A propos des déclencheurs Pre-Query et Post-Query ..............................................................43
Rappel .......................................................................................................................................................... 44
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 3
PARTIE I : L’environnement d’Oracle
TP 1 Connexion des Clients au Serveur Oracle,
Pratique de SQL avec l'éditeur SQL-Worksheet
1. Objectifs
-
Etablir une connexion entre une machine de la salle de TP et le serveur Oracle (outil SQLNet
Easy Config)
-
Pratique de SQL sur une petite BD
-
Utilisation de l'éditeur SQL Worksheet d’Oracle
2. Environnement
2.1. Les outils Oracle
Tous les outils Oracle sont logés dans le répertoire Programme \ Bases de données \ Oracle. Les
produits suivants sont installés pour l’administration d’une instance Oracle,:
Administration Toolbar bandeau d'icônes d'outils Oracle
Backup Manager
outil de sauvegarde / restauration d'une base de données Oracle (Non
étudié)
Storage Manager
définit les paramètres de stockage de la base de données : espaces
logique, fichiers de données…
Schema Manager
équivalent graphique de la partie LDD de SQL qui gère les tables, vues,
synonymes
SQL Worksheet
éditeur SQL plein écran
Data Manager
permet de transférer des données vers et depuis des bases de données
Oracle (Export/Import). Nécessite l'activation de l'outil Enterprise
Manager
Instance Manager
permet de démarrer ou d'arrêter une base de données, de visualiser et de
modifier ses paramètres d'initialisation, de créer des configurations de
démarrage personnalisées, de gérer les transactions en suspens et les
sessions utilisateur
Security Manager
gestion des utilisateurs (noms, mots de passe, droits…) de toutes les
bases de données Oracle
SQL Plus
éditeur "ligne" de SQL et de son extension SQL + (requêtes SQL
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 4
paramètrées, éditions de tableau plus soignée…)
SQL-Net8 Easy Config
gestion des connexions entre une machine cliente et les bases de
données Oracle 8 installées sur les serveurs accessibles depuis cette
machine
Admin ODBC
gestion d'une liaison entre un logiciel client (ex. ACCESS) et un serveur
Oracle qui respecte le protocole ODBC (Open Data Base Connectivity)
De plus Programme \ Bases de données \ Oracle donne accès à :
-
Documentation en ligne : ce répertoire contient les entrées, au format HTML ou autres, vers la
documentation en ligne des produits Oracle.
-
Forms & Reports : regroupe plusieurs outils de développement d'applications, dont Forms
Builder.
Les écrans de connexion à un outil Oracle ont toujours la forme suivante :
Cet écran permet de ce connecter au serveur Oracle. Il permet à l’utilisateur de s’identifier
(gestion des droits d’accès). De plus, il permet d’indiquer l’instance (base de données) à laquelle
l’utilisateur souhaite se connecter. Cela ce fait au travers d’un service : c'est un alias facile à retenir
par l’utilisateur, qui regroupe à la fois un nom de protocole réseau, l’adresse d’un serveur et le nom
d’une instance sur ce serveur. Attention, suivant le client (SQL+, SQL Worksheet,…) les libellés
des trois zones peuvent changer. Ainsi l’intitulé “Service” peut s’appeler “Chaine de connection”;
dans tous les cas, la valeur attendue est le nom d’un service.
2.2. La base de données support des TP
Voici le schéma Oracle simplifié de la base de données relatif à la Coupe du Monde de
football 1998 :
EQUIPES ( NE, NATIONALITE, PALMARES )
JOUEURS ( NJ, NOM, PRENOM, POSITION, MAILLOT, NE )
MATCHS ( NM, LIEU, DATE_R, NE_ RECEVANTE, NE_DEPLACANTE, ENJEU,
SCORE_RECEVANTE, SCORE_ DEPLACANTE)
PARTICIPANTS (NJ, NM, NB_BUTS, CARTON_JAUNE, CARTON_ROUGE )
Remarques :
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 5
La table JOUEURS précise les noms des joueurs et l’équipe à laquelle ils appartiennent. La
table MATCHS enregistre pour chaque match l’enjeu (Finale, 1 /2, 1/4, 1/8 ...), la date, le lieu, les
numéros des deux équipes ayant participé et leur score respectif. L’équipe ayant gagné correspond à
celle ayant le score le plus élevé. La table PARTICIPANTS enregistre pour chaque match la liste
des joueurs ayant participé avec le nombre de buts marqués et deux booléens précisant s’ils ont eu
un carton jaune et/ou un carton rouge.
3. Actions
3.1. A propos de l'outil SQL-Net8 Easy Config
L'outil SQL-Net8 Easy Config permet de créer, sur une machine cliente, les noms des services
nécessaires pour accéder aux serveurs des bases de données. Les noms de services ont été
configurés directement à l’aide d’un éditeur dans le fichier TNSNAMES.ora de chaque machine. Le
principe de fonctionnement de SQL-Net8 Easy Config est retracé ci-dessous.
Les actions décrites par l'enchaînement d'écrans figurant ci-dessous créent un service appelé "IGSI".
Se service permet, dans l'environnement Oracle de la machine cliente, de se connecter avec l'une des
bases Oracle installées sur le serveur "sip-oracle". Cette base (instance) porte le nom "IGSI". Le
protocole de communication utilisé est le standard TCP/IP.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 6
La céation des trois services igsi, said et stat via SQL-Net8 Easy Config a produit le résultat
suivant dans le fichier TNSNAMES.ora :
IGSI.world =
(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP) (Host = sip-oracle) (Port = 1521) )
(CONNECT_DATA = (SID = IGSI)
 nom d’une instance sur le serveur sip-oracle
)
)
SIAD. world =
(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP) (Host = sip-oracle) (Port = 1521) )
(CONNECT_DATA = (SID = = SIAD)
)
)
STAT. world =
(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP) (Host = sip-oracle) (Port = 1521) )
(CONNECT_DATA = (SID = STAT)
)
)
3.2. Lancer l'outil Administration Toolbar
Bandeau d'outils Oracle, (personnalisable avec le clic droit)
3.3. Lancer l'outil SQL Worksheet
L’écran est découpé en 2 zones : la zone des commandes SQL (partie basse), la zone d’affichage des
résultats (partie haute). Les 4 dernières icônes de la palette d’outils de la zone des commandes SQL
permettent
1.
2.
3.
4.
d’exécuter la commande tapée dans la zone
d’afficher l’historique des commandes de la session
d’afficher dans la zone la commande précédente
d’afficher dans la zone la commande suivante.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 7
3.4. Lancer l'outil Admin ODBC
Créer une nouvelle source de données respectant le protocole ODBC sur la machine locale, à
laquelle on donnera le nom "igsi oracle". La liaison ODBC sera pilotée par le driver Oracle ODBC
driver. Cette liaison utilisera le service "igsi" déjà créé.
3.5. Lancer Microsoft ACCESS
Créer une nouvelle BD sous votre compte, que vous enregistrerez sous le nom "mondial" et choisir
Fichier \ Données externes \ Lier les tables. Choisir le type de fichier "Bases de données ODBC"
(dernier de la liste). Choisir la source de données "igsi oracle" déjà créée. Entrer le compte
"mondial" et son mot de passe.
Sélectionner les tables de la BD "Coupe du monde" qui apparaîtront comme dans la figue qui suit.
Consulter les tables comme à l'accoutumée. Comparer le mode d'interaction avec l'outil SQL
Worksheet. On prendra soin de consulter les schémas des tables, sous ACCESS et sous SQLWorkSheet. Dans SQL-WorkSheet, les schémas sont accessibles en consultant les deux vues TAB
et COL du dictionnaire des données :
Select * from tab
Select * from col
et en invoquant la requête
describe nom_de_table
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 8
3.6. Traiter les Requêtes SQL suivantes
A partir de la BD implantée sur le compte "mondial", écrire et tester les requêtes suivantes en SQL.
On utilisera l'outil clients Oracle SQL-WorkSheet.
Q1)
Qui a eu un carton rouge en 1 /2 finale ? Afficher le nom et le prénom.
Q2) Nombre total de buts marqués par Zidane ?
Q3) Liste des joueurs de l’équipe de France qui n’ont pas joué en finale ? Afficher les noms et les
prénoms.
Q4) Liste des joueurs appartenant à la même équipe que Ronaldo ? Afficher les noms et les
prénoms.
Q5) Afficher le nom et le prénom de chaque joueur ainsi que le nombre total de buts marqués ?
Q5') Q5 sans les joueurs dont on n'a pas enregistré le nombre de buts marqués
Q6) Qui a gagné la finale ? Afficher le pays.
Q7) Liste des joueurs qui ont joués les mêmes matchs que Zidane.
Q8) Nom(s) du (ou des) joueur(s) ayant marqué le plus de buts en un match ?
Q9) Nom(s) du (ou des) meilleur(s) buteur(s) ?
Q10) Reculer d'un jour la date des 1 /2 finales.
Q11) Ajouter une équipe portant votre nom. Editer la liste des équipes avec le nombre de joueurs
qui y sont engagés. Que constatez-vous ? Chercher dans la documentation en ligne de SQL les
informations sur le "outer join" et modifier votre requête en conséquence.
Q12) Ajouter 2 joueurs, votre binôme et vous, à cette équipe, ajouter un match éliminatoire entre
cette équipe et l'équipe du binôme voisin, et inscrire les 4 joueurs dans ce match. Quelle est la
suite d'actions à entreprendre pour pouvoir supprimer l'équipe que vous avez créée ?
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 9
TP 2 Traduction de règles de gestion à l'aide de
déclencheurs Bases de Données
1. Objectifs
Pratique du langage PL/SQL
Conception de déclencheurs Bases de Données
2. Environnement
Le schéma de la base de données relatif à la Coupe du Monde de football 1998 est légèrement
modifié pour prendre en compte les gègles de gestion énnoncées ci-dessous :
EQUIPES ( NE, NATIONALITE, PALMARES )
JOUEURS ( NJ, NOM, PRENOM, POSITION, MAILLOT, NE, NB_MATCH_EXPULSE )
MATCHS ( NM, LIEU, DATE_R, NE_ RECEVANTE, NE_DEPLACANTE, ENJEU,
SCORE_RECEVANTE, SCORE_ DEPLACANTE, SCORE_VALIDE)
PARTICIPANTS (NJ, NM, NB_BUTS, CARTON_JAUNE, CARTON_ROUGE )
Règles de gestion
1. Lorsqu'un joueur obtient 2 cartons jaunes au cours d'un match, il reçoit un carton rouge.
2. Lorsqu'un joueur reçoit un carton rouge, il est expulsé pour les deux matchs à venir.
3. Evidemment, un joueur expulsé ne doit pas pouvoir être sélectionné pour participer à un match.
Afin de faire respecter ces règles, les deux attributs suivants ont été rajoutés respectivement
dans les tables JOUEURS et MATCHS :

NB_MATCH_EXPULSE indique à tout moment pour combien de matchs le joueur est encore
expulsé depuis le dernier match effectivement joué (le score est validé) par sont équipe.

SCORE_VALIDE vaut 1 si le match a été effectivement joué.
3. Actions
Proposez les déclencheurs nécessaires pour faire respecter ces règles de gestion.
1. Créez sous votre compte votre propre base de donnée à l'image du schéma ci-dessus.
2. Utilisez le Schema Manager pour créer vos déclencheurs.
Nous vous conseillons d'exécuter la requête SQL générée (create or replace trigger) sous le
Worksheet pour détecter les éventuelles erreurs de syntaxe.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 10
Rappel : Syntaxe de la création d’un déclencheur
CREATE [OR REPLACE] TRIGGER [<nomSchéma>.]<nomTrigger>
(AFTER |BEFORE ) ( INSERT |
DELETE |
UPDATE [OF <nomAttribut> [,<nomAttribut>]*])
ON [<nomschéma>.]<nomTable>
[REFERENCING (OLD |NEW) as (<old_perso>|<new_perso>)
[,(OLD |NEW) as (<old_perso>|<new_perso>)]
([FOR EACH ROW] | [FOR EACH STATEMENT])
[WHEN <condition>]
<module PL/SQL> ;
Avec :
<nomSchéma> : nom d’un utilisateur
<nomTrigger> : nom que l’on souhaite donner au déclencheur
<nomAttribut> : nom d’un attribut
<nomTable> : nom d’une table
<old_perso> : nom donné par l’utilisateur pour renommer le mot clé OLD
<new_perso> : nom donné par l’utilisateur pour renommer le mot clé NEW
<module PL/SQL> : module PL/SQL sans entête.
Exemple :
Faire respecter la règle de gestion suivante :
1. Le nombre de buts marqués par un joueur lors d’un match ne peut diminuer.
-> A chaque mise à jour du nombre de buts dans la table participants, avant que la mise à jour ne
soit effectuée, on compare l’ancien nombre de buts avec le nouveau, si le nouveau nombre est plus
petit, on remet l’ancien nombre de buts et on signale une erreur à l’application.
CREATE OR REPLACE TRIGGER "MONDIAL"."AUGMENTER_BUTS"
BEFORE UPDATE OF "NB_BUTS" ON "MONDIAL"."PARTICIPANTS"
REFERENCING OLD AS O NEW AS N
FOR EACH ROW
WHEN (n.nb_buts<o.nb_buts)
BEGIN
:n.nb_buts := :o.nb_buts;
raise_application_error(-20100, 'le nombre de buts ne peut diminuer');
END;
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 11
Solution
Action 3.1 :
Il faut créer quatre déclencheurs.
Le premier est associé à la table PARTICIPANTS sur mise à jour du nombre de cartons
jaunes. Si ce nombre atteint 2, le nombre de cartons rouges est mis à 1.
Le deuxième déclencheur est associé à la table PARTICIPANTS sur mise à jour du nombre de
cartons rouges. Si ce nombre atteint 1, l'attribut NB_MATCH_EXPULSE de la table JOUEURS est
mis à 3 pour le joueur en question.
Le troisième déclencheurs est associé à la table MATCHS. Lorsque l'attribut
SCORE_VALIDE prend la valeur 1, on décrémente de 1 l'attribut NB_MATCH_EXPULSE pour
tous les joueurs expulsés des deux équipes participantes.
Le quatrième déclencheurs est associé à la table PARTICIPANTS. Lorsque l'on tente d'insérer
une ligne dans cette table, l'insertion est refusée si le joueur en question est expulsé.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 12
Action 3.2 :
CREATE OR REPLACE TRIGGER "MONDIAL"."EXPULSER_jaune"
BEFORE INSERT OR UPDATE OF "CARTON_JAUNE" ON "MONDIAL"."PARTICIPANTS"
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
IF :new.carton_jaune = 2
THEN
:new.carton_rouge := 1;
virez_le(:new.nj);
/* dbms_output.put_line('joueur '||:o.nj||' expulsé'); */
END IF;
END;
CREATE OR REPLACE TRIGGER "MONDIAL"."EXPULSER_rouge"
BEFORE INSERT OR UPDATE OF "CARTON_ROUGE" ON "MONDIAL"."PARTICIPANTS"
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
WHEN (new.carton_rouge=1)
BEGIN
update joueurs
set NB_MATCH_EXPULSE = 3
where nj = :old.nj;
END;
CREATE TRIGGER "MONDIAL".Expuls_moins_un
AFTER UPDATE OF "SCORE_VALIDE" ON "MONDIAL"."MATCHS"
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
WHEN (new.score_valide=1)
begin
update joueurs
set NB_MATCH_EXPULSE = NB_MATCH_EXPULSE - 1
where NB_MATCH_EXPULSE >= 1
and ne in (:new.ne_deplacante, :new.ne_recevante);
end;
CREATE or replace TRIGGER "MONDIAL".ne_peut_participer
BEFORE INSERT ON "MONDIAL"."PARTICIPANTS"
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
declare
virer number := 0 ;
begin
select 1 into virer
from joueurs
where nj = :new.nj
and NB_MATCH_EXPULSE >= 1;
if virer = 1 then
raise_application_error(-20101, 'Ce joueur est exclu');
end if;
exception WHEN_NO_DATA_FOUND then
null;
end;
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 13
PARTIE II : Développement d’applications
clientes avec Developper 2000
TP 3 Le concept de formulaire dans Developer 2000
1. Rappel : Les trois principaux objets d’un module
Un formulaire est un élément interactif qui permet d’insérer, consulter et/ou mettre à jours des nuplets stockés dans une base de données. ORACLE propose un environnement de développement
permettant de concevoir de tels formulaires appelé Developer 2000, qui s’appuie lui même sur un
environnement de plus bas niveau appelé FORMS. Dans cet environnement, les formulaires sont
gérés au travers de modules. Un module peut se définir comme un programme évènementiel capable
de prendre en charge les interactions d’un utilisateur avec plusieurs formulaires, au même titre
qu’une application écrite en Visual Basic. Chaque module constitue un client du serveur base de
données ORACLE.
Developer 2000 propose trois outils appelés Form Builder, Form Compiler et Forms Runtime,
permettant respectivement de concevoir des formulaires, générer le code permettant d’exécuter ces
formulaires dans l’environnement d’un système d’exploitation donné (Windows, Unix, IBM ...) et
d’exécuter ce code. Afin de faciliter le développement, Forms Builder permet d’exécuter
directement les formulaires en cours de conception, en vue de les tester. Nous utiliserons donc
principalement cet outil.
Un module est constitué d’objets dont les propriétés permettent de définir le comportement de
l’application. Certaines propriétés sont modifiables dynamiquement, permettant de modifier le
comportement de l’application en cours d’exécution. Certains objets réagissent à des évènements,
permettant de déclencher l’exécution de programmes. Les 3 principaux objets constituant un
module sont rapidement décrits ci-dessous :
Bloc (Block)
Un bloc est une structure logique permettant de manipuler des valeurs stockées dans
l’environnement du module. Un bloc est lui même constituée d’éléments (les conténaires
des valeurs). Un bloc peut être créé manuellement ou automatiquement par le biais
d’assistants (wizzards). Un bloc peut être définit en mono ou en multi-lignes. Une propriété
permet de fixer ce nombre de lignes. Ainsi un bloc est assimilable à un tableau temporaire
stocké dans l’espace mémoire du module (côté client). Un bloc peut être associée une table
ou une vue de la base de données (stockée côté serveur). Dans ce cas, on dit qu’il est basé.
Deux propriétés permettent de spécifier si le block est basé ou non, ainsi que la table ou la
vue à laquelle il est associé. Un block basé est assimilable à une fenêtre dans laquelle on
peut faire défiler côté client des n-uplets stockés dans la base de donnée. Pour un bloc basé,
Forms prend en charge toutes les interactions en mise à jour entre les valeurs skockées dans
le tableau temporaire et celles stockées dans la base de donnée.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 14
Elément (Item)
Un élément sert à gérer des données ou à déclencher des commandes Forms ou des Triggers,
en fonction de son type. Par exemple, un élément de type texte permet de stocker et
d’afficher une valeur pour le bloc auquel il appartient ; un élément de type bouton permet de
déclencher des actions comme passer à la ligne suivante dans le bloc courant. Un élément de
type texte possède une propriété garantissant qu’il soit simplement consultable ou
consultable et modifiable. De plus, une propriété fixe si l’élément doit être visible ou
invisible. Pour être visible, il doit être localisé sur un canevas. Cette localisation est elle
même définie par une propriété.
Canevas (Canvas)
Un canevas est une surface sur laquelle sont positionnés des éléments, graphiques ou textes
destinés à être affichés à l’écran. Un canevas peut être découpé en onglets. Un canevas est
affiché dans une fenêtre.
Un module appelé MODULEMONDIAL est présenté figure 1. Ce module gère un formulaire
basique permettant de consulter et modifier les n-uplets de la table JOUEURS. Les objets
constituant ce module peuvent être explorés sur la sous-fenêtre de gauche, intitulée Navigateur
d’Objets. Ainsi, ce formulaire est constitué d’un bloc appelé JOUEURS basé sur la table
JOUEURS. Ce bloc comporte 6 éléments de type texte permettant de stocker localement et
d’afficher les attributs d’un joueur. Tous les éléments sont localisés sur le canevas
CANEVASMONDIAL, représenté sur la sous-fenêtre de droite. On peut observer que ce canevas
est constitué d’un onglet appelé ONGLETJOUEURS. Sur cet onglet, les éléments sont placés dans
un cadre appelé CADREJOUEURS. On peut aussi observer que le bloc JOUEURS est mono-ligne
car chaque élément apparaît une seule fois dans le cadre.
Figure 1 : Un module et la fenêtre du canevas correspondant
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 15
Ce formulaire a été généré automatiquement, mais l’emplacement des éléments peut être
modifié directement en cliquant sur les objets représentés dans la fenêtre du canevas. De même les
propriétés des éléments du module ainsi généré peuvent être modifiées au travers de la sous-fenêtre
intitulée Palette de propriétés. Par exemple pour interdire la mise à jour des N° des joueurs, on
peut sélectionner l’élément NJ, puis cliquer sur le bouton de droite de la souris pour faire apparaître
les propriétés de cet élément comme illustré figure 2. La propriété Interrogation seulement peut
alors être mise à vrai.
Figure 2 : propriétés de l’élément NJ
2. Création d'un formulaire basique
Le TP utilise la base de donnée Mondial. Vous pouvez consulter son schéma à l’aide du
Schema Manager ou dans le Navigateur d’objets (section Objets de la base de données) si vous êtes
connectés..
Créer le formulaire permettant de consulter les joueurs et de les modifier.
Lancer Oracle/Forms and Reports/Form Builder et utiliser les assistants. Vous devez au
passage cliquer sur le bouton parcourir pour sélectionner la table JOUEURS. Cette action va
déclencher une connexion à ORACLE pour contrôler que vous avez accès au schéma de la base de
données. Choisissez les colonnes qui vous intéressent puis demandez à utiliser l’assistant de
présentation. Lors de la création du canevas, demandez un canevas avec onglet.
Cliquer sur le bouton feu vert pour générer et tester le code du nouveau module (client).
Cliquer sur le bouton exécuter l’interrogation pour visualiser les joueurs.
Modifier les propriétés de l’élément NOM pour interdire la mise à jour du nom des joueurs.
Jouer avec la fenêtre et le canevas.
3. Création d’un formulaire comportant un sous formulaire
Créer le formulaire permettant d’afficher les équipes avec leurs joueurs respectifs. Le module
correspondant sera composé de deux blocs dont un bloc maître et un bloc esclave (voir figure 3). Le
bloc maître est le bloc EQUIPES. Il pilote l’affichage des n-uplets dans le bloc esclave qui est le
bloc JOUEURS. En effet, le bloc JOUEURS ne doit afficher que les joueurs de l’équipe en cours
dans le bloc EQUIPES. Ainsi les deux blocs sont reliés par une relation dont le but est d’exprimer
la condition de jointure entre les deux tables : EQUIPES.NE = JOUEURS.NE. Le bloc EQUIPES
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 16
est un bloc de type formulaire qui affiche un n-uplet à la fois, tandis que le bloc JOUEURS est un
bloc de type tabulaire. On fera en sorte que ce dernier affiche 11 joueurs et possède un ascenseur.
Créer ce formulaire en utilisant les assistants comme suit. Créer tout d’abord un bloc basique
pour la table EQUIPES comme précédemment. Ajouter ensuite un bloc pour la table joueurs :
cliquer sur l’élément blocs de données pour le sélectionner, puis cliquer sur le bouton « + » pour
ajouter un bloc et demander à utiliser l’assistant. N’oubliez pas de sélectionner la colonne NE à la
création du bloc Joueurs sans quoi la jointure avec le bloc Equipes ne pourrait pas être
réalisée. Par contre il n’est pas nécessaire d’afficher cette colonne puisqu’elle est déjà renseignée
dans le bloc EQUIPES.
Si la liaison entre les deux blocs ne s’est pas faite automatiquement, double cliquez sur le
composant relation du bloc EQUIPES, sélectionnez le bloc joueurs comme bloc esclave et entrez la
condition de jointure comme sur la figure 4.
Figure 3 : Formulaire avec un bloc maître et un bloc détail
Figure 4 : Création de la relation entre 2 blocs
Remarquez la création de deux déclencheurs destinés à la synchronisation de l’affichage dans
les deux blocs (voir figure 5).
Figure 5 : Déclencheurs et relation permettant de gérer la synchronisation entre 2 blocs
Remarques :
1. Le déclencheur ON-POPULATE-DETAILS remplit le bloc détail à chaque changement de
ligne dans le bloc maître. Si le code de ce déclencheur est altéré, la synchronisation entre
les deux blocs ne se fait plus. Dans ce cas, nous suggérons de supprimer la relation (ce qui
a pour effet de supprimer les déclencheurs associés), et de la recréer.
2. Le déclencheur PRE-DELETE exécute un code redondant avec la contrainte d’intégrité
référentielle déclarée dans la base de données pour l’attribut NE de la table JOUEURS.
Vous pouvez le supprimer.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 17
4. Exercice
Concevoir des blocs simples de gestion pour chaque relation de votre base Mondial et de
gestion pour chaque relation de la base Mondial du compte Mondial. Vous disposerez ainsi de
moyens personnels pour consulter et mettre à jour facilement les deux bases.
Concevoir des blocs Maîtres Détails
-
Equipes  Joueurs (de chaque équipe) Participants (de chaque joueur)
-
Matchs  Participants (de chaque match)
-
Equipes  Matchs (que les équipes soient recevantes ou déplaçantes)  Participants (de
chaque match de chaque équipe)
Remarque : le dernier formulaire comporte une double jointure entre EQUIPES et MATCHS sur
ne_recevante et ne_deplacante. Il n’est pas possible de spécifier ce type de jointure dans une
relation (maitre/detail). Avec l’expérience, vous apprendrez à gérer ce genre de problème (voir
TP6).
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 18
TP 4 Listes de valeurs (LOV) et déclencheurs d'applications.
Création de boutons et de déclencheurs associés à des LOV
écrits en PL/SQL
1 Rappel : la gestion des clés étrangères
Les clés étrangères sont utilisées pour relier les tables entre elles. Les clés étrangères posent
généralement deux problèmes à l’utilisateur final :
1- elles ne peuvent contenir que des valeurs existant dans la table référencée (contrainte
d’intégrité référentielle). En effet une clé étrangère contenant une valeur qui n’existe pas
dans la table référencée n’a pas de sens.
Par exemple : si l’attribut NE de la table Joueurs contient une valeur qui n’existe pas dans
la table Equipes, c’est équivalent à dire que ce joueur appartient à une équipe inexistante.
2- les clés sont souvent peu parlantes pour l’utilisateur final.
Par exemple : quelle est la nationalité de l’équipe 3 ?
Dans le cas d’un formulaire avec sous formulaire ces problèmes sont généralement
contournés. Par exemple, le formulaire Equipes_Joueurs créé au TP précédent constitue le moyen
idéal pour saisir une équipe avec tous ses joueurs. La clé étrangère NE de la table JOUEURS est
renseignée automatiquement en fonction de la clé de l’équipe courante dans le formulaire principal.
Dans le cas d’un formulaire simple, ces deux problèmes peuvent être résolus en utilisant un
objet "liste de valeurs" (LOV pour List Of Values) dans le module :
Une LOV est une interrogation permettant d’afficher l’ensemble des valeurs possibles et d’en
sélectionner une. De plus, une LOV permet d’associer à ces valeurs des information plus parlantes
qu’une valeur de clé (par exemple, nationalité de l’équipe).
2. création d’un formulaire exploitant une LOV
2.1. Exercice : expérimentation des LOV
1. Créer un formulaire simple pour la table PARTICIPANTS au moyen des assistants.
Cette table contient deux clés étrangères : NJ et NM.
2. Création d’une LOV pour NJ :
Double cliquez sur l’objet LOV de votre module pour créer la LOV et sélectionnez l’assistant.
Au travers de cet assistant, demandez à ce que cette LOV permette d’afficher NJ, NOM,
PRENOM pour chaque JOUEUR (nom et prénom sont affichés à titre d’information).
L’assistant doit générer une requête du type : SELECT NJ, NOM, PRENOM FROM JOUEURS ;
Toujours au travers de l’assistant, précisez ensuite que la valeur NJ ramenée par cette LOV
devra être assignée à l’élément NJ du bloc PARTICIPANT.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 19
Renommez la LOV créée en ‘LOV_NJ’
3. Tester le module. Lorsque l’on clique sur l’élément NJ, la barre d’état indique qu’une liste de
valeurs est disponible (voir figure 1). Pour afficher cette liste appuyez sur la touche fonction F9.
Cette barre d’état indique que l’on est positionné sur le 1 er N-uplet
et que l’élément en cours peut être instancié via une liste de valeurs
Figure 1 : barre d’état
Remarque
Pour connaître les touches fonction de Forms et leur rôle, activez le menu Aides/Touches.
2.2. Concepts autour des LOV
Dans Developper 2000, une LOV est un objet permettant de faire afficher une liste de valeurs (table)
dans une fenêtre et de récupérer une valeur choisie par l’utilisateur dans cette liste, pour la stocker
dans un élément d’un bloc. La liste des valeurs affichée correspond au résultat d’une requête SQL.
Cette requête est elle même stockée dans un autre objet appelé record group ou plus simplement
GROUPE. Le même GROUPE (la même requête) peut être associé à plusieurs LOV. Par contre,
une LOV est spécifique à un ELEMENT dans un BLOC, pour lequel elle ramène une valeur. La
propriété correspondance de colonne d’une LOV à pour objet de désigner cet élément récepteur.
Enfin, l’ELEMENT récepteur d’une LOV comporte une propriété appelée LOV permettant de
désigner la LOV qui lui est associée. Ces trois aspects sont détaillés ci-dessous.
2.2.1. Record Group
Le rôle d’un record group est de stocker une requête associée à une ou plusieurs LOV. Cette
requête permet de construire dynamiquement les valeurs de cette liste lorsque la LOV est invoquée.
La figure 2 illustre la création d’un groupe d’enregistrement. La requête associée sélectionne les n°
des joueurs de la table JOUEURS, mais aussi les noms et prénoms des joueurs, qui sont plus
parlants pour l’utilisateur final. Une fois le GROUP créé, la requête est stockée dans la propriété
Query de ce dernier.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 20
Figure 2 : La requête associée à une LOV
2.2.2. Correspondance de Colonne
La spécification des colonnes de la requête qui seront affectées à un élément d’un bloc, se fait
au moyen de la propriété correspondance de colonne de l’objet LOV en cours de création. Vous
pouvez consulter ou modifier cette spécification en cliquant sur le bouton Suite de cette propriété.
Dans notre cas, il faut associez la colonne NJ du record group de la LOV à l’élément NJ du bloc
PARTICIPANTS (voir figure 3).
Au travers de cette propriété, il est en fait possible de spécifier un récepteur pour chaque
colonne résultat de la requête du record group. Cela est principalement utile lorsqu’une LOV est
définie pour une clé étrangère comportant plusieurs attributs. (Cela pourrait aussi être utilisé pour
renseigner des éléments non-basés, mais ce genre d’utilisation conduit à des formulaires avec des
anomalies de comportement.)
Figure 3 : Association de la colonne NJ de la liste à l’élément NJ du bloc
2.2.3. Association d’une LOV à un Elément
La propriété LOV d’un élément permet d’indiquer qu’une LOV lui est associée et donc de
remonter cette information à l’utilisateur dans la barre d’état du formulaire. Elle permet en plus,
d’identifier la LOV qu’il faut aller chercher lorsque l’utilisateur appuie sur la touche F9. Dans notre
cas, la propriété LOV de l’élément NJ a été modifiée pour signaler qu’une LOV est associée à cet
élément, il s’agit de la LOV ‘LOV4’ (voir figure 4).
Figure 4 : affecter une LOV à un Élément
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 21
3. Améliorer l'utilisation d'une LOV
Vous pouvez remarquer que la LOV créée pour NJ n'est pars facile à exploiter par l'utilisateur
final. En effet, pour sélectionner un joueur par son nom, il faut faire défiler toute la liste avec un
ascenseur. Or, la fenêtre d'affichage d'une LOV comporte un outil de recherche. Cet outil
s'applique exclusivement à la première colonne affichée dans la LOV et nécessite que cette colonne
soit triée. Ainsi, il serait souhaitable de réécrire la requête associée à la LOV afin d'afficher en
premier le nom du joueur. La requête modifiée et la LOV associée sont affichées ci-dessous.
Attention, il est nécessaire de trier le résultat, pour que les outils de recherche fonctionnent.
SELECT ALL JOUEURS.NOM, JOUEURS.PRENOM, JOUEURS.NE, JOUEURS.NJ
FROM JOUEURS
ORDER BY 1
Exercice :
Améliorez la LOV LOV_NJ. Expérimentez-la.
Créez une LOV pour NM.
4. Déclencheurs d’application (triggers)
Il existe plusieurs solutions pour contrôler l’affichage de la LOV associée à un élément
(élément), dans de but d’éviter à l’utilisateur non averti d’avoir à utiliser la touche F9. Ces solutions
utilisent toutes des déclencheurs provoquant l’exécution d’un programme écrit en PL/SQL
lorsqu’un événement intervient au niveau de l’application.
Un déclencheur d’application (trigger) est définit par trois principaux composants :
-
son type (évènement associé) ;
son code (bloc PL/SQL);
son niveau de définition (objet de l’interface auquel il est associé).
Les objets auxquels il est possible d’associer des déclencheurs sont les modules de type
"application forms", les blocs et les éléments des blocs (items).
Les évènements permettant d’activer un déclencheur d’application varient en fonction du type
d’objet de l’interface auquel est associé le déclencheur (module, bloc ou élément). Ces évènements
sont classés en cinq catégories :
-
PRE- se déclenche avant une action forms ;
POST- se déclenche après une action forms ;
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 22
-
WHEN- se déclenche au même moment qu’une action forms (permet d’augmenter les
fonctionnalités de forms) ;
ON- se déclenche à la place d’une action forms ;
KEY- se déclenche à la place d’une touche fonction standard.
Pour obtenir la liste de ces événements avec leur signification précise utiliser la commande
de l’aide de developper/2000 Aide/Form builder Rubrique d’aide/Référence PL/SQL /Déclencheurs.
Vous pouvez aussi passer par la commande rechercher du menu Aide/Form builder Rubrique
d’aide.
Rappelons que la structure d’un bloc PL/SQL est la suivante :
[DECLARE]
- variables, constantes, curseurs, expressions utilisateurs
BEGIN
- Ordres SQL
- Instructions de contrôle PL/SQL
[EXCEPTIONS]
- Traitements à effectuer lors des erreurs
END ;
Chaque instruction à l’intérieurs du bloc se termine par un point virgule.
Comme indiqué ci-dessus, un bloc PL/SQL comporte 3 sections (DECLARE, BEGIN et
EXCEPTIONS) parmi lesquelles seule la section BEGIN est obligatoire. Dans le cas ou les blocs
DECLARE et EXCEPTIONS sont omis, les lignes BEGIN et END sont facultatives.
4.1. Exemple : contrôler l’affichage d’une LOV dans un formulaire
DECLARE rep BOOLEAN;
BEGIN
IF :participants.nj is null THEN
bell;
rep := show_lov ('LOV_NJ');
END IF;
END;
4.1.1 Préfixe ":" pour désigner les éléments des blocs
Dans le code ci-dessus, rappelons que dans l’expression :participants.nj le préfixe ‘:’
permet de désigner l’élément NJ du bloc PARTICIPANTS par opposition à l’attribut NJ de la table
PARTICIPANTS (lui même désigné par l’expression participants.nj).
4.1.2. Fonctions utiles pour les application écrites en PL/SQL
est une commande très utile de PL/SQL. Il en existe bien d’autres de ce type. Il est
impossible de les connaître toutes par cœur. Pour les retrouver et apprendre à les utiliser, exploitez
la commande d’aide Aide/Form builder Rubrique d’aide/Référence PL/SQL /Packages intégrés de
Form Builder/Liste alphabétique.
show_lov
4.2. Déclencheurs d’application et déclencheurs base de données
Deux aspect fondamentaux différencient les déclencheurs d’application des déclencheurs base
de données :
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 23
1. Qui contrôle les évènements déclencheurs ;
2. Où les code est-il exécuté.
Dans le cas des déclencheurs d’application, les évènements sont contrôlés par l’application cliente
et le code PL/SQL est exécuté sur la machine cliente.
Dans le cas des déclencheurs base de données les évènements sont contrôlés par le serveur base de
données et le code PL/SQL est exécuté sur la machine serveur.
5. Gestion automatique de l’affichage d’une LOV
Trois solutions vous sont proposées pour afficher la LOV associée à NJ.
comportent des inconvénients.
Certaines
Solution 1 : création d’un déclencheur WHEN-MOUSE-DOUBLECLICK pour l’item NJ
Pour créer ce déclencheur cliquez sur le + de l’élément NJ afin de faire apparaître l’objet
déclencheur. Double cliquez sur cet objet puis choisissez l’événement voulu dans la fenêtre du
déclencheur (WHEN-MOUSE-DOUBLECLICK). Un éditeur PL/SQL s’ouvre automatiquement.
Saisissez le code ci-dessous. Cliquez sur le bouton compiler de la fenêtre du déclencheur pour
tester la syntaxe de votre code.
DECLARE rep BOOLEAN;
BEGIN
bell;
rep := show_lov ('LOV_NJ');
END;
/* LOV_NJ est le nom de la LOV */
Testez ce déclencheur. L’inconvénient de cette solution est que l’utilisateur doit connaître la
convention qui consiste à double cliquer sur l’élément pour faire apparaître la LOV.
Solution 2 : création d’un déclencheur PRE-TEXT-ITEM pour l’élément NJ
Procédez comme précédemment, en exploitant en événement de type PRE-TEXT-ITEM et en
utilisant le code ci-dessous.
declare
recstat
VARCHAR2(20) := :System.record_status;
rep boolean;
BEGIN
IF ( recstat = 'NEW' or recstat = 'INSERT' ) THEN
rep:=show_lov('LOV_NJ');
END IF;
END;
Un événement de type PRE-TEXT-ITEM se déclenche chaque fois que l’élément en question
devient actif (par le biais de la touche tabulation, suite à un click souris, suite au passage à la ligne
suivante, suite à l’ouverture du module …). Ainsi, afficher une LOV systématiquement alors que
l’utilisateur peut exploiter le formulaire simplement pour consulter une table, peut devenir un
élément perturbateur.
Examinez la condition ci-dessus. Elle consulte une variable système (nom de variable préfixé
par ‘ :’) indiquant l’état de l’enregistrement en cours. S’il n’est pas en mode insertion (l’utilisateur
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 24
vient de créer une nouvelle ligne), le déclencheur ne fait rien. Cette solution pallie l’inconvénient
mentionné ci-dessus. Par contre, la LOV ne s’affichera qu’en mode création et pas en mode mise à
jour. Respecter les majuscules dans 'NEW' et dans 'INSERT'.
Solution 3 : création d’un bouton avec un déclencheur WHEN-BUTTON-PRESSED
Ajoutez un nouvel élément au bloc PARTICIPANTS. Modifier la propriété type d’élément de
ce nouvel élément, choisissez bouton de commande. Dans la propriété libellé de cet élément,
introduisez la chaîne '\/' (back-slach, slash). Dans la propriété canevas, mentionnez le canevas de
votre formulaire. Renommez cet élément Liste_Joueurs. Ajoutez le déclencheur à l’élément avec le
même code que dans la solution 1. Modifiez le canevas pour que le bouton prenne l’aspect d’une
boîte liste à côté de NJ.
Attention, pour que ce bouton puisse être utilisé, il faut que son élément soit placé en premier
dans la liste des éléments du bloc. Observez les anomalies d’utilisation de ce bouton. Ces
anomalies peuvent être évitées en positionnant la propriété obligatoire de NJ à faux.
6. Gestion de LOV dynamiques
En fonction du contexte de l’application, il peut être utile qu’une LOV n’affiche que les
données pertinentes d’une table. Par exemple, une fois le numéro de match saisi dans une ligne du
formulaire de la table participants, il serait souhaitable que la LOV permettant de saisir un numéro
de joueur restreigne l’affichage aux joueurs d’une des équipes participant au match choisi. Ainsi, la
requête associée à cette LOV doit être modifiée automatiquement, à chaque appel de la LOV, afin
de prendre en compte le numéro de match saisi dans l’élément NM courant du bloc
( :PARTICIPANTS.NM).
Pour
cela,
nous
allons
utiliser
la
fonction
POPULATE_GROUP_WITH_QUERY décrite ci-dessous.
POPULATE_GROUP_WITH_QUERY :
Lorsque vous utilisez cette procédure interne, l'interrogation indiquée devient
l'interrogation par défaut du groupe, et s'exécute chaque fois que la procédure interne
POPULATE_GROUP est appelée. Si l'instruction SELECT échoue, Form Builder retourne
un numéro d'erreur ORACLE. Si l'interrogation réussit, cette procédure interne retourne 0
(zéro). Les lignes extraites remplacent les lignes du groupe d'enregistrements.
Syntaxe
FUNCTION POPULATE_GROUP_WITH_QUERY
(id_groupeenreg RecordGroup,
interrogation VARCHAR2);
FUNCTION POPULATE_GROUP_WITH_QUERY
(nom_groupeenreg VARCHAR2,
interrogation VARCHAR2);
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 25
Modifiez le code du déclencheur associé au bouton de la LOV LOV_NJ comme suit :
declare
rep boolean; -- retour show_lov
myQuery varchar2(250); -- texte de la requete
acqQuery Integer; -- retour de l'execution de la requete
begin
myQuery := 'Select nom, prenom, nj from
joueurs, matchs
where nm =:participants1.nm
and ne in (ne_recevante, ne_deplacante)
order by nom, prenom';
-- redéfinition du groupe conformément à la requete SQL "myQuery"
acqQuery := populate_group_with_query('RG_LOV_NJ', myQuery);
/* 0 --> tt c'est bien passe */
if(acqQuery = 0) then
rep := show_lov('LOV_NJ');
else
raise form_trigger_failure;
end if;
end;
Autre version :
declare
rep boolean;
begin
/* L'exemple ci-dessous montre comment remplacer l’interrogation
** d’un groupe d'enregistrements lors de l'exécution : */
IF (Populate_Group_With_Query
( 'LOV_NJ',
'SELECT NOM, PRENOM, NE, NJ
FROM JOUEURS
where ne in (select ne_recevante
from matchs
where nm = :participants.nm
union
select ne_deplacante
from matchs
where nm = :participants.nm)
order by 1 '
)
)
<> 0 THEN
RAISE form_trigger_failure;
END IF;
rep:=show_lov('LOV_NJ');
END;
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 26
TP 5 Synchronisations entre les Blocs de données
(FORMS) et la Base de Données
1. Notion de Bloc basé / non basé
Un Bloc basé est destiné à permettre la consultation et/ou la mise à jours de données issues
d'une table stockée dans la base de données ou d'une vue. Le nom de la source de données est une
des propriétés Base de données du Bloc. Au niveau du client Forms, le bloc se comporte comme un
tableau de lignes appelées enregistrements (records), capables d'accueillir des n-uplets issues de la
source de données. La structure de ces n-uplets est décrite par les éléments basés du bloc, ceux dont
la propriété Elément base de données est positionnée à la valeur Oui.
1.1. Remplissage du tableau d’un Bloc avec des données stockées dans la Base de données
Au démarrage de l'application cliente, ce tableau d'enregistrements est vide. La procédure
prédéfinie "EXECUTE_QUERY" permet d'extraire les données de la source pour remplir le tableau
du bloc actif. Cette procédure peut être activée par l'utilisateur en utilisant l'un des moyens
suivants : la commande du menu Interrogation/Exécuter ; le bouton exécuter interrogation ; la
touche F8. Elle peut aussi être appelée dans un déclencheur d'application.
Par défaut, l'interrogation permettant d'instancier le tableau d'un bloc a la structure suivante :
SELECT
INTO
FROM
ALL E1, E2,…,En
:E1, :E2,…, :En
source ;
où :E1, :E2,…, :En sont les noms des Eléments basés du Bloc et source est le nom de la
source de données (table ou vue) du Bloc.
Cette requête porte sur une seule table. Elle peut comporter une clause WHERE si l'utilisateur
a utilisé l'un des moyens suivants : la commande du menu Interrogation/Entrer ; le bouton Entrer
Interrogation ou la touche F7, avant d'exécuter l'interrogation. Il est aussi possible de spécifier une
clause WHERE par défaut pour un Bloc, via la propriété Base de données du bloc nommée
DEFAULT_WHERE. Cette propriété peut être modifiée par programmation dans un déclencheur en
utilisant la procédure SET_BLOCK_PROPERTY spécifiée ci-dessous :
SET_BLOCK_PROPERTY ('NOM_DU_BLOC', DEFAULT_WHERE, 'prédicat');
Il peut arriver que l’action EXECUTE_QUERY retourne le message d'erreur suivant :
"Exécution de l'interrogation impossible". Cela peut être du au fait que l'un des Eléments basés du
Bloc porte un nom ne correspondant pas à un attribut de la source de données. Cela peut aussi être
du à une erreur dans la clause WHERE. Pour voir le texte de la requête qui a été envoyée au serveur
base de données, appeler la commande du menu "Aide/Afficher erreur".
1.2. Actualisation de la Base de données via le tableau d’un Bloc
Par défaut, l’utilisateur a le droit de modifier, insérer ou supprimer des lignes dans le tableau
correspondant à un Bloc de données. Il est possible de restreindre les droits de mise à jour d’un
utilisateur, au niveau de l’application Forms, via les propriétés Base de données Insertion autorisée,
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 27
Mise à jour autorisée et Suppression autorisée. Ces propriétés peuvent être réglées au niveau du
Bloc ou de ses Eléments.
Si l'application lui en donne l'autorisation, l'utilisateur peut insérer, modifier des lignes dans le
tableau d'un bloc. La procédure prédéfinie COMMIT_FORM permet de transférer les données
modifiées du tableau du bloc vers la source de données (dans la base de données). Cette procédure
peut être activée par l'utilisateur en utilisant l'un des moyens suivants : la commande du menu
Action/Enregister ; le bouton Enregister ; la touche F10. Elle peut aussi être appelée dans un
déclencheur d'application.
Etant donné que les mises à jours dans le tableau du bloc, sont effectuées indépendamment de
la Base de données, la commande transactionnelle ROLLBACK ne peut pas fonctionner pour
annuler les mises à jour effectuées au travers d'une application Forms. La seule solution pour
annuler les mises à jour, consiste à vider le tableau du Bloc puis à ré-exécuter l’interrogation du
Bloc. Forms Builder propose les procédures suivantes permettant respectivement de vider tous les
Blocs, vider le Bloc actif, vider l’enregistrement actif, vider l’Elément actif :
CLEAR_FORM (mode_commit) ;
CLEAR_BLOCK (mode_commit) ;
CLEAR_RECORD ;
CLEAR_ITEM ;
mode_commit admet plusieurs options (cf. documentation). L’option NO_VALIDATE permet
de vider le ou les blocs sans transmettre les mises à jour à la base de données.
2. Concevoir l’interface d’un Bloc basé
2.1. Utilisation d'alertes
Une alerte est un objet rattaché à un module, permettant d'afficher un message dans une boîte
de dialogue. Ainsi, une alerte est dotée de la propriété message destinée à recevoir le message qui
doit être affiché dans la boîte de dialogue. Vous pouvez changer un message d'alerte lors de
l'exécution en lançant la procédure interne SET_ALERT_PROPERTY. Cette option vous permet de
réutiliser le même objet alerte, mais d'afficher un message différent chaque fois qu'il est appelé.
Enfin, la fonction ci-dessous permet de d'afficher la boîte de dialogue d'une alerte :
reponse
numerique */
:=
Show_Alert
('NOM_DE_L_ALERTE');
/*
reponse
est
de
type
Ouvrir l’application Equipes_Joueurs comportant un bloc maître pour la table EQUIPES et un
bloc esclave pour la table JOUEURS (cf. TP précédents).
Créer une alerte générique appelée ALERTE_GENERIQUE. Y associer le message 'Coucou'.
Créer la fonction Msg_Box via le Navigateur d’objets (section Unités de programme) avec le
code ci_dessous :
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 28
FUNCTION Msg_Box (titre varchar2, message varchar2) RETURN boolean
IS
rep integer;
BEGIN
set_alert_property('ALERTE_GENERIQUE', TITLE, titre);
set_alert_property('ALERTE_GENERIQUE', ALERT_MESSAGE_TEXT, message);
rep := show_alert('alerte_generique');
if rep = ALERT_BUTTON1 then
return true;
else
return false;
end if;
END;
Créer un bouton dans le bloc EQUIPES permettant d'afficher le message ‘Vous avez cliqué
sur le bouton message’ dans une fenêtre intitulée ‘ceci est un message’ .
declare
rep boolean;
begin
rep:= Msg_Box ('ceci est un message','Vous avez cliqué sur le bouton
message');
end
2.2. Contrôle du remplissage d’un Bloc
Créer un déclencheur WHEN-NEW-FORM-INSTANCE affecté au module. Ce bloc
contiendra le code suivant :
BEGIN
EXECUTE_QUERY;
/* REMARQUE
** Il aurait été possible de Simuler l'action sur la touche F8 en
** utilisant la procédure interne : DO_KEY.
** Exemple :
**
Do_Key('EXECUTE_QUERY');
*/
END;
Exécuter la forme. Que se passe-t-il ?
Il se peut que le Bloc actif ne soit pas le bloc EQUIPES au moment ou l'application Forms
démarre. Le Bloc actif à l’ouverture de l’application est le premier Bloc qui apparaît dans la liste
des Blocs de données du module Forms. Deux solutions sont possibles pour activer en premier le
Bloc EQUIPES :
a) Le mettre en premier dans l’élément Blocs de données en le faisant glisser avec la souris.
b) Ajouter dans le déclencheur WHEN-NEW-FORM-INSTANCE la commande :
Go_Block('EQUIPES');
avant la commande :
EXECUTE_QUERY;
Tester les deux solutions.
A titre d’exercice, mettez un prédicat erroné (ex : ‘trurlututu’) dans la propriété Clause
WHERE ou Clause ORDER BY d’un Bloc, puis observez la requête SQL envoyée au serveur via la
commande du menu "Aide/Afficher erreur".
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 29
2.3. Restriction dynamique du remplissage d’un Bloc
Objectif : restreindre dynamiquement les données chargées dans le bloc PARTICIPANTS.
Assurez-vous que votre module possède un onglet permettant d’afficher les participants d’un
match. Si ce n’est pas le cas, ouvrez le module PARTICIPANTS créé au cours du TP concernant les
LOV. Créez un bloc non basé appelé RESTRICTION comportant un Elément (Text Item)
permettant à l’utilisateur de saisir un n° de match et deux boutons RESTREINDRE et
TOUT_AFFICHER permettant respectivement, sur un simple clic, de n’afficher que les participants
du match choisi par l’utilisateur ou de tout afficher. La marche à suivre est détaillée ci-dessous.
Créez un bloc appelé RESTRICTION sans utiliser les assistants. Signaler qu’il ne s’agit pas
d’un bloc basé sur la base de données (cf. figure 1). Y ajouter un Elément (Text Item) et deux
boutons. N’oubliez pas d’afficher l’élément et les boutons dans le canevas (ou l’onglet) courant.
Figure 1 : Création d’un bloc non basé
Renommez l’Elément de saisie "NM".
Affectez aux boutons des libellés appropriés. Ajoutez au bouton "RESTREINDRE" un
déclencheur WHEN-BUTTON-PRESSED avec le code ci-dessous :
declare
rep boolean;
begin
if ltrim(rtrim(:restriction.nm)) is null then
bell;
rep := Msg_Box('message', 'Vous n''avez pas sélectionné de match !');
else
SET_BLOCK_PROPERTY ('PARTICIPANTS', DEFAULT_WHERE,
'NM = '||:restriction.nm);
Go_Block ('PARTICIPANTS');
Execute_Query;
end if;
end;
Associez au bouton TOUT_AFFICHER le code ci-dessous :
begin
SET_BLOCK_PROPERTY ('PARTICIPANTS', DEFAULT_WHERE,'');
Go_Block ('PARTICIPANTS'); /* remarquez que la clause WHERE est vide */
Execute_Query;
end;
2.4. Afficher des données calculées dans un Elément non basé
Ajouter au bloc JOUEURS un nouvel Elément non basé (i.e. avec la propriété "Elément de
base de données" positionnée à FAUX). Renommer cet Elément "CALCULE" et initialiser
correctement ses propriétés Canevas et Page d'onglet. Placer cet élément au bon endroit dans le
canevas et lui affecter une étiquette.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 30
Affecter à cet élément un déclencheur WHEN_MOUSE_CLICK avec le code suivant :
select sum(nb_buts) into :joueurs.calcule
from participants
where nj = :joueurs.nj;
Pensez à ajouter des buts dans la table PARTICIPANTS pour tester votre code.
Problème :
Malheureusement, avec ce déclencheur, il faut cliquer sur l’élément calculé pour afficher son
contenu. Pour que toutes les instances de l'Elément CALCULE se remplissent automatiquement, il
faut exécuter l’instruction SELECT pour chaque enregistrement du bloc JOUEURS. A cette fin, on
pourrait utiliser le code suivant :
Go_Block ('JOUEURS');
First_Record; -- se positionner sur la première ligne du bloc
WHILE :System.Last_Record <> 'TRUE' LOOP
-- tant que les lignes
select sum(nb_buts) into :joueurs.calcule -- contiennent des valeurs …
from participants
where nj = :joueurs.nj;
Next_Record; -- passer à la ligne suivante
END LOOP;
Comment exécuter ce code automatiquement ?
Solution : déclencheur Post-Query
La solution la plus simple consiste à utiliser le déclencheur Post-Query dont la spécification
est rappelée ci-dessous :
Lorsqu'une interrogation est ouverte dans le bloc, le déclencheur PostQuery est activé chaque fois que Form Builder extrait un enregistrement
d'un bloc. Il est donc activé une fois par enregistrement placé dans le
tableau des enregistrements du bloc.
Pour cet exemple, le déclencheur Post-Query doit être déclaré au niveau du bloc EQUIPES
avec le même code que pour le déclencheur WHEN_MOUSE_CLICK. La boucle n’est plus
nécessaire !
3. Corrélation entre les Blocs, l’actualisation de la Base de Données et les
Déclencheurs d’application
3.1 Création d’un bloc non basé comportant les boutons Valider et Annuler
Objectif : Comprendre pourquoi une bouton ANNULER dans l’application ne peut pas se
contenter d’exécuter l’ordre SQL ROLLBACK. Application : créer deux boutons VALIDER et
ANNULER pour les blocs imbriqués EQUIPES et JOUEURS.
Créer un bloc appelé "COMMANDES" sans utiliser les assistants. Signaler qu’il ne s’agit pas
d’un bloc basé sur la base de données (cf. figure 1). Y ajouter deux boutons dotés chacun d'un
déclencheur WHEN_BUTTON_PRESSED permettant respectivement de valider une transaction
avec ses mises à jour ou d’abandonner la transaction. Affecter à ces boutons des libellés appropriés.
Utiliser la commande d’aide pour trouver les instructions PL/SQL que le bouton VALIDER doit
exécuter. N’oubliez pas d’afficher ces boutons dans le canevas (ou l’onglet) courant.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 31
Le code que doit exécuter le bouton ANNULER est le suivant :
message('abandon des mises à jour');
--rollback; /* ne marche pas dans un déclencheur d’application */
--raise form_trigger_failure; /* ne marche pas dans ce cas */
Go_Block ('EQUIPES');
Clear_Block(No_validate);
Go_Block ('EQUIPES');
Execute_Query;
Remarque 1 :
La commande MESSAGE permet d'afficher un message à l'utilisateur. L'inconvénient de cette
commande est le suivant : selon le contexte, le message s'affiche soit en bas de l'écran dans la barre
d'états, soit dans une fenêtre spécifique avec un bouton OK. La deuxième solution est préférable,
mais on ne peut pas l'imposer. C’est pourquoi il vaut mieux utiliser les alertes.
Remarque 2 :
Après une mise à jour dans le Bloc JOUEURS, le bouton Annuler génère un message
"Voulez-vous enregistrer les mises à jours". Ce message provient du fait des synchronisations
maître/détail avec le Bloc EQUIPES. Le même message s'affiche après une mise à jour dans le Bloc
JOUEURS puis un changement d'équipe dans le Bloc maître. Pour éviter cet inconvénient, il faut
utiliser l'instruction Clear_Form qui ne contraint pas l'application à revenir dans le Bloc EQUIPES.
Par contre, cette instruction a pour effet de vider aussi les Blocs des autres onglets.
Clear_form (no_validate); /* vide le bloc */
Go_Block('EQUIPES');
/* se positionnne sur le bloc maître*/
Execute_Query;
3.2. Gestion d’une contrainte d’intégrité au niveau de l’application
Objectif : Comprendre les corrélations qu’il peut y avoir entre l’actualisation des blocs et la
mise en œuvre de contraintes d’intégrité au niveau de l’application. Dans ce but, nous allons
introduire une contrainte qui interdit l’insertion de plus de 14 joueurs pour une équipe.
Ouvrez l’application EQUIPES_JOUEURS. Créez deux déclencheurs associés au module, le
premier de type PRE-COMMIT et le second de type POST-FORMS-COMMIT.
Leur associer le code :
Declare
V_ne
V_nbj
reponse
number (11);
number;
number;
Begin
V_ne := :EQUIPES.NE;
select count(nj) into V_nbj
from joueurs
where ne = V_ne;
Set_Alert_Property('ALERTE_GENERIQUE',ALERT_MESSAGE_TEXT,
'Précomit : '||V_nbj);
reponse := Show_Alert('ALERTE_GENERIQUE');
End ;
Exécutez le module, insérer 2 joueurs à une équipe, validez et observez les messages :
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 32
 les joueurs ne sont pas encore insérés dans la BD au moment de l'exécution du "précommit".
Modifiez le déclencheur POST-FORMS-COMMIT pour qu’il exécute le code suivant :
Declare
V_ne
number (11);
V_nbj
number;
trop_joueurs Exception;
Begin
V_ne := :EQUIPES.NE;
/* Numéro de l’équipe actuelle */
select
into
from
where
count(nj)
V_nbj
joueurs
ne = V_ne;
/* combien y a-t-il de joueurs */
/* pour cette équipe ?
*/
If v_nbj > 14 Then
/* si ce nombre est > 14 on
*/
raise trop_joueurs; /* lève une exception
*/
End If;
Exception
When trop_joueurs Then
/* cette exception affiche un message */
message ('plus de 14 joueurs');
bell; bell;
--ROLLBACK; /* Rappel : pas possible ici. */
Raise form_trigger_failure;
End;
Remarques :
a) Avec ce code, les joueurs insérés en trop restent affichés. L’utilisateur peut supprimer
quelques joueurs, puis tenter de revalider sa transaction.
b) Si l'on insère des joueurs dans une équipe, puis on essaie de changer d’équipe pour insérer
d’autres joueurs, Forms force la validation de la transaction.
 Le code ci-dessus est correct. En effet, si la transaction n’était pas validée à chaque
changement d’équipe, le comptage du nombre de joueurs pour l’équipe en cours ne
serait pas suffisant.
3.3. Création d’une contrainte sur un élément comparant les valeurs avant maj et après maj
Objectif : Exploiter le fait que les valeurs mises à jours dans les formulaires ne sont pas
actualisées dans la BD tant que la transaction n’a pas validé. Exemple : introduction une
contrainte qui interdit de diminuer le nombre de buts marqués par un joueur.
Ouvrir l’application PARTICIPANTS. Créer un déclencheur associé à l’élément NB_BUTS
de type WHEN-VALIDATE-ITEM et lui associer le code :
Declare
old_nb_buts number;
Begin
Select
nb_buts
Into
old_nb_buts
/* la BD n’a pas encore été mise à jour */
From
participants
Where
nj = :participants.nj and nm = :participants.nm;
If :participants.nb_buts < old_nb_buts Then
message('le nombre de buts doit augmenter');
raise form_trigger_failure;
End If;
End;
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 33
Tester si la nouvelle contrainte est bien appliquée.
3.4. Question de cours
Peut-on remplacer les deux contraintes d'application créées dans les exercices VI et VII par
des contraintes d'intégrité déclarées au niveau du schéma de la base de données ? Quels sont les
avantages et inconvénients des deux solutions ?
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 34
TP 6 Gestion spécifique des blocs maître/détail
lorsqu'il n'y a pas de jointure directe entre les
blocs. Gestion du remplissage des blocs détails à
l'aide de déclencheurs écrits en PL/SQL
1 Position du problème
On souhaite produire le formulaire de la figure 1, qui présente chaque match avec les deux
équipes impliquées et la liste des participants de chaque équipe :
Figure 1 : un formulaire non standard
Ce formulaire est constitué de 5 Blocs de données et de 2 Relations Maître-Détails :

le bloc MATCHS est basé sur la table Matchs,

les blocs EQUIPE_REC et EQUIPE_DEP sont basés sur la table Equipes et sont en relation
Maître Détails avec MATCHS avec les conditions respectives :
"Matchs.ne_recevante=Equipe_rec.ne" et "Matchs.ne_deplacante=Equipe_dep.ne".

les blocs PARTICIPANTS_REC et PARTICIPANTS_DEP sont basés sur la table Participants
et ne sont en relation avec aucun autre bloc.
Le remplissage dans des blocs PARTICIPANTS_REC et PARTICIPANTS_DEP ne peut être
géré par une Relation Maître-Détails car il n'y a pas de condition de jointure directe entre le bloc
MATCHS et ces 2 blocs. Il n'est pas non plus possible de créer une relation Détails-Détails entre les
blocs EQUIPE_REC et PARTICIPANTS_REC (resp EQUIPE_DEP et PARTICIPANTS_DEP).
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 35
2. Utiliser la fonction Execut_Query dans un déclencheur déclencheur ONPOPULATE-DETAILS
La solution consiste à remplir ces 2 blocs par programmation dans le déclencheur ONPOPULATE-DETAILS du block MATCHS, en le complétant de la façon suivante :
-- Note : le code en italique correspond au code généré par Form_Builder pour les
-- deux relations : 'MATCHS.MATCHS_EQUIPE_REC' et 'MATCHS.MATCHS_EQUIPE_DEP'
--- Begin EQUIPES_REC detail program section
-IF ( (:MATCHS.NE_RECEVANTE is not null) ) THEN
rel_id := Find_Relation('MATCHS.MATCHS_EQUIPE_REC');
Query_Master_Details(rel_id, 'EQUIPE_REC');
--- Début du Code à ajouter au déclencheur
--
Set_Block_Property('PARTICIPANTS_REC',DEFAULT_WHERE,
'nj in (select nj from joueurs where ne = '
||:MATCHS.NE_RECEVANTE
||') and nm = '
||:MATCHS.NM);
go_block('PARTICIPANTS_REC');
execute_query;
--- Fin du Code à ajouter au déclencheur
--
END IF;
--- End EQUIPES_REC detail program section
---- Begin EQUIPES_DEP detail program section
-IF ( (:MATCHS.NE_DEPLACANTE is not null) ) THEN
rel_id := Find_Relation('MATCHS.MATCHS_EQUIPE_DEP');
Query_Master_Details(rel_id, 'EQUIPE_DEP');
--- Début du Code à ajouter au déclencheur
--
Set_Block_Property('PARTICIPANTS_DEP',DEFAULT_WHERE,
'nj in (select nj from joueurs where ne = '
||:MATCHS.NE_DEPLACANTE
||') and nm = '
||:MATCHS.NM);
go_block('PARTICIPANTS_DEP');
execute_query;
--- Fin du Code à ajouter au déclencheur
--
END IF;
--- End EQUIPES_DEP detail program section
--
REMARQUES :
Cette solution est basée sur l'utilisation de l'instruction EXECUTE_QUERY pour
remplir les éléments des blocs PARTICIPANTS_REC et PARTICIPANTS_DEP avec les valeurs
stockées dans la base de données. Dans la logique de Form Builder, c'est la seule solution possible
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 36
pour remplir des blocs basés. En effet toute affectation d’un élément (même par programme) est
considérée comme une mise à jour.
3. Création d’un bloc non basé affichant des données
Une autre solution consiste à débaser les blocs PARTICIPANTS_REC et
PARTICIPANTS_DEP et à remplir ces blocs par programmation en envoyant des requêtes SQL
à la Base de données et en affectant les résultats des requête aux éléments des blocs. Attention, les
blocs étant non-basés, les mises à jours réalisées par l'utilisateur ne sont pas prises en compte
automatiquement par Forms. Cette solution est moins élégante, mais le code correspondant est
instructif. A titre d'illustration, ce code est le suivant :
--- Begin default relation declare section
-DECLARE
recstat VARCHAR2(20) := :System.record_status; -- état du record courant
startitm VARCHAR2(61) := :System.cursor_item;
-- élément "actif"coutant
rel_id
Relation;
--- Début des déclarations des nouvelles variables pour le remplissage des
-- blocs PARTICIPANTS_REC et PARTICIPANTS_DEP
CURSOR c_rec is
SELECT
p.nj, nb_buts, carton_jaune, carton_rouge
FROM
participants p, joueurs j
where
p.nj=j.nj
and j.ne= :matchs.ne_recevante
and p.nm = :matchs.nm
ORDER BY
p.nj ;
CURSOR c_dep is
SELECT
p.nj, nb_buts, carton_jaune, carton_rouge
FROM
participants p, joueurs j
where
p.nj=j.nj
and j.ne= :matchs.ne_deplacante
and p.nm = :matchs.nm
ORDER BY
p.nj ;
--- Fin des déclarations de variables ajoutées au déclencheur
--- End default relation declare section
---- Begin default relation program section
-BEGIN
IF ( recstat = 'NEW' or recstat = 'INSERT' ) THEN
RETURN;
END IF;
--- Begin EQUIPES detail program section
-IF ( (:MATCHS.NE_RECEVANTE is not null) ) THEN
rel_id := Find_Relation('MATCHS.MATCHS_EQUIPES');
Query_Master_Details(rel_id, 'EQUIPES');
--- Début du Code à ajouter au déclencheur pour afficher PATCICIPANTS
--
Go_Block ('PARTICIPANTS_REC');
Clear_Block;
First_Record;
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 37
OPEN c_rec;
Fetch
c_rec
into
:participants.nj,
:participants_rec.nb_buts,
:participants_rec.carton_jaune,
:participants_rec.carton_rouge;
While c_rec%found Loop
Next_Record;
Fetch c_rec into
:participants_rec.nj,
:participants_rec.nb_buts,
:participants_rec.carton_jaune,
:participants_rec.carton_rouge;
End Loop;
Close c_rec;
--- Fin du Code à ajouter au déclencheur
--
END IF;
--- End EQUIPES detail program section
---- Begin EQUIPES1 detail program section
-IF ( (:MATCHS.NE_DEPLACANTE is not null) ) THEN
rel_id := Find_Relation('MATCHS.MATCHS_EQUIPES1');
Query_Master_Details(rel_id, 'EQUIPES1');
--- Début du Code à ajouter au déclencheur pour afficher PATCICIPANTS1
--
Go_Block ('PARTICIPANTS1');
Clear_Block;
First_Record;
Open c_dep;
-- message('ne = '||:matchs.ne_recevante);
-- message('nm = '||:matchs.nm);
Fetch c_dep into
:participants1.nj,
:participants_dep.nb_buts,
:participants_dep.carton_jaune,
:participants_dep.carton_rouge;
While c_dep%found Loop
Next_Record;
Fetch c_dep into
:participants_dep.nj,
:participants_dep.nb_buts,
:participants_dep.carton_jaune,
:participants_dep.carton_rouge;
End Loop;
Close c_dep;
--- Fin du Code à ajouter au dévlencheur
--
END IF;
--- End EQUIPES1 detail program section
-IF ( :System.cursor_item <> startitm ) THEN
Go_Item(startitm);
Check_Package_Failure;
END IF;
END;
--- End default relation program section
--
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 38
4. Exercice : Ajout d'éléments calculés à ce formulaire
Modifier le module pour que :
1. le module présente les noms des joueurs comme sur la figure 2,
2. on puisse inscrire de nouveaux joueurs dans chacune des 2 équipes, en utilisant une LOV sur
chaque bouton "inscrire".
On modifiera la propriété Visible des éléments à masquer et on ajoutera le code suivant à un
déclencheur POST-QUERY attaché aux 2 blocs PARTICIPANTS_DEP et PARTICIPANTS_REC
-- pour le bloc PARTICIPANTS_DEP
select nom
into :participants_dep.nomj
-– "nomj" présente les noms des joueurs
from joueurs
where :participants_dep.nj=nj;
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 39
Figure 2 : formulaire permettant de saisir "intelligemment" les participants d'un match
TP 7 Compléments
1. Les variables globales
Une variable globale est une variable Forms Builder dont la valeur est accessible aux
déclencheurs et aux sous-programmes de tout module qui est actif pendant la session en cours. Une
variable globale stocke une chaîne d'un maximum de 255 caractères.
Les variables globales ne sont pas déclarées formellement comme les variables locales
PL/SQL. Vous initialisez une variable globale la première fois que vous lui attribuez une valeur, par
exemple :
:GLOBAL.my_var := TO_CHAR(:order.total * .85);
Pour faire référence à une variable globale, faites précéder son nom du mot GLOBAL et du
caractère ":". L'exemple ci-dessous indique une variable globale transmise sous la forme d'un
paramètre réel à un appel de procédure :
calculate_discount(TO_NUMBER(:GLOBAL.my_var));
Faire référence à une variable globale qui n'a pas été initialisée par une affectation entraîne la
génération d'une erreur d'exécution.
Pour détruire une variable globale et libérer la place qu'elle occupe en mémoire, utilisez la
procédure interne ERASE. :
Erase(':GLOBAL.my_var');
2. Les variables système
Pour certains déclencheurs, il est utile de connaître l’état du formulaire. Par exemple, le
déclencheur WHEN-NEW-RECORD-INSTANCE est activé à chaque fois que l’on change de ligne
dans un bloc. Il peut alors être utile de savoir si l’on est en mode "mise à jour" ou en mode
"interrogation". En effet, c’est le seul moyen de savoir si l’on est effectivement en train de créer une
nouvelle ligne, pour déclencher une action dans ce cas. On peut ainsi tester l’état du bloc avec la
variable système :System.record_status comme suit :
DECLARE
recstat
VARCHAR2(20) := :System.record_status;
BEGIN
IF ( recstat = 'NEW') THEN
-- action à faire en cas de création de n-uplet
END IF;
END ;
La liste les variables système, avec des exemples d’utilisation, peut être consulté via la
commande "Aide" de FormsBuilder sous la rubrique :
Génération et exécution d’une application Forms
Ajout de PL/SQL à une application Forms
Utilisation de PL/SQL dans Form Builder
A propos de l’utilisation de PL/SQL dans Form Builder
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 40
A propos des variables système de Form Builder
Cette liste est la suivante :
SYSTEM.BLOCK_STATUS
SYSTEM.COORDINATION_OPERATION SYSTEM.CURRENT_BLOCK
SYSTEM.BLOCK_STATUS,
SYSTEM.COORDINATION_OPERATION,
SYSTEM.CURRENT_BLOCK,
SYSTEM.CURRENT_DATETIME,
SYSTEM.CURRENT_FORM,
SYSTEM.CURRENT_ITEM,
SYSTEM.CURRENT_VALUE,
SYSTEM.CURSOR_BLOCK,
SYSTEM.CURSOR_ITEM,
SYSTEM.CURSOR_RECORD,
SYSTEM.CURSOR_VALUE,
SYSTEM.DATE_THRESHOLD*,
SYSTEM.EFFECTIVE_DATE*,
SYSTEM.EVENT_WINDOW,
SYSTEM.FORM_STATUS,
SYSTEM.LAST_QUERY,
SYSTEM.LAST_RECORD,
SYSTEM.MASTER_BLOCK,
SYSTEM.MESSAGE_LEVEL*,
SYSTEM.MODE,
SYSTEM.MOUSE_BUTTON_PRESSED,
SYSTEM.MOUSE_BUTTON_SHIFT_STATE,
SYSTEM.MOUSE_ITEM,
SYSTEM.MOUSE_CANVAS,
SYSTEM.MOUSE_X_POS,
SYSTEM.MOUSE_Y_POS,
SYSTEM.MOUSE_RECORD,
SYSTEM.MOUSE_RECORD_OFFSET, SYSTEM.RECORD_STATUS , SYSTEM.SUPPRESS_WORKING*,
SYSTEM.TAB_NEW_PAGE,
SYSTEM.TAB_PREVIOUS_PAGE ,
SYSTEM.TRIGGER_BLOCK,
SYSTEM.TRIGGER_ITEM , SYSTEM.TRIGGER_RECORD.
Les eules variables système auxquelles vous pouvez affecter explicitement des valeurs sont les
quatre suivies d'une astérisque (*).Toutes les autres ne peuvent qu'être lues.
Exemple : utilisation de la variable système SYSTEM.CURSOR_BLOCK :
Supposons que vous vouliez créer, au niveau application Forms, un déclencheur KeyNXTBLK dont la navigation dépend du bloc en cours. Cette fonction est exécutée par le
déclencheur décrit ci-dessous, à l'aide de la variable SYSTEM.CURSOR_BLOCK stockée dans une
variable locale.
DECLARE
cour_bloc VARCHAR2(30);
BEGIN
cour_bloc :=
:System.Cursor_Block;
IF cour_bloc = 'MATCHS'
THEN Go_Block('EQUIPES');
ELSIF cour_bloc = 'EQUIPES'
THEN Go_Block('PARTICIPANTS');
ELSIF cour_bloc = 'PARTICIPANTS'
THEN Go_Block('MATCHS');
END IF;
END;
3. Gestion de l’affichage de messages d’erreur
Le problème : les messages d’erreur affichés par Oracle en cas d’erreur sur une mise à jour
effectuée au travers d’un formulaire. Par exemple, lorsque l’on insère une valeur de clé déjà
existante dans une table, oracle affiche le message d’erreur suivant :
FRM-40508: Erreur ORACLE : insertion (insert) d’enregistrement impossible
Il est possible de changer ce message en utilisant le déclencheur ON-ERROR :
Ouvrir le module EQUIPES_JOUEURS. Associer à ce module un déclencheur ON-ERROR.
Générer le code suivant pour ce déclencheur :
DECLARE
code_erreur
BEGIN
NUMBER
:= ERROR_CODE;
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 41
IF (code_erreur = 40508) THEN
Message ('Duplication de la valeur d''une clé'); -- attention à ''
END IF;
END;
Il est possible d’affiner le message d’erreur en associant des déclencheurs identiques aux
blocs. Associer deux déclencheurs aux blocs EQUIPES et JOUEURS. Les messages d’erreur
deviennent respectivement : 'Insertion d''un n° d''équipe en double' et 'Insertion
d''un n° de joueur en double'.
Il n’est pas nécessaire de supprimer le déclencheur du module. En effet, un déclencheur de
bloc est prioritaire sur un déclencheur de module, de même qu’un déclencheur d’élément est
prioritaire sur un déclencheur de bloc.
4. Utilisation de séquences et des sous-programmes PL/SQL
4.1. Objectif des séquences : éviter les problèmes de saisie sur les clés.
Exemple 1 : saisie d’une valeur de clé qui existe déjà.
Exemple 2 : deux utilisateurs saisissent une nouvelle valeur de clé en même temps (arrivée
d’un nouveau client). Ce deuxième exemple nous ramène au problème de l’exemple 1.
Une séquence est un objet de la base de données (comme une table ou une vue), qui permet de
générer automatiquement des numéros croissant en évitant les problèmes de concurrence d’accès. Il
s’agit d’une table à une ligne et une colonne. On dispose de deux commandes pour manipuler une
séquence :
-
CURRVAL retourne la valeur courante de la séquence,
-
NEXTVAL génère une nouvelle valeur et la retourne.
4.2. Application : génération automatique du n° d'équipe
Appeler le Schema Manager, se positionner sur l’objet "séquences" et cliquer sur le bouton
‘+’. Créer une séquence de nom SEQ_NE et donner la valeur de départ (valeur minimum) pour
générer le n° d’équipe.
Créer un déclencheur WHEN-BUTTON-PRESSED associé à l’élément NE du bloc
EQUIPES, avec le code suivant :
DECLARE
FUNCTION Numero_suivant
RETURN NUMBER IS
CURSOR next_seq IS SELECT seq_equipes.NEXTVAL FROM DUAL;
tmp NUMBER;
BEGIN
OPEN next_seq;
FETCH next_seq INTO tmp;
CLOSE next_seq;
RETURN tmp;
END;
BEGIN
:equipes.ne := Numero_suivant;
END;
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 42
L’inconvénient de ce déclencheur est que l’utilisateur doit cliquer de lui même sur l’élément
pour générer un nouveau n° d’équipe. Pour générer automatiquement un n° d’équipe, deux
solutions sont possibles.
a) générer un déclencheur WHEN-VALIDATE-ITEM avec le même code, associé à l’élément
NE.
Attention : ce déclencheur n'est activé que si l'élément a une valeur (même pour tester la
contrainte not null) => mettre une valeur par défaut dans la propriété valeur initiale de l'élément
(ex : 9999999999).
b) générer un déclencheur WHEN-NEW-RECORD-INSTANCE avec le code suivant :
DECLARE
recstat
VARCHAR2(20) := :System.record_status;
BEGIN
IF ( recstat = 'NEW') THEN
:equipes.ne := Nouveau_NE -- action à faire en cas de création de tuple
END IF;
END ;
Remarque : Nouveau_NE est le nom d’une fonction créée au niveau de l’objet "Unités de
programmes" du module en cours, qui a le code suivant :
FUNCTION Nouveau_NE
RETURN NUMBER IS
CURSOR next_seq IS
SELECT seq_equipes.NEXTVAL FROM DUAL;
tmp NUMBER;
BEGIN
OPEN next_seq;
FETCH next_seq INTO tmp;
CLOSE next_seq;
RETURN tmp;
END;
5. A propos des déclencheurs Pre-Query et Post-Query
Les déclencheurs Pre-Query et Post-Query vous permettent de contrôler le traitement des
interrogations. Ils peuvent être définis au niveau application Forms ou bloc. Dans la plupart des cas,
vous les attacherez à des blocs spécifiques pour contrôler les fonctions d'interrogation de ces blocs.
Le déclencheur Pre-Query est activé immédiatement avant que FormsRuntime transmette
l'instruction SELECT à la base de données, lorsque l'utilisateur a défini l'enregistrement, par
exemple en entrant des critères de recherche en mode Saisie interrogation.
Dans un déclencheur Pre-Query, l'enregistrement exemple défini par les critères
d'interrogation est l'enregistrement en cours, ce qui signifie que votre code de déclencheur peut lire
et définir les valeurs des éléments de l'enregistrement exemple en utilisant la syntaxe standard
:nom_bloc.nom_élément.
Un déclencheur Pre-Query peut être utilisé pour désactiver les conditions d'interrogation qui
pourraient ne pas être valides. Lorsqu'une application Forms est en mode Saisie interrogation, la
validation normale est suspendue et aucun déclencheur de validation n'est activé, comme il le serait
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 43
en mode Normal. Le déclencheur Pre-Query vous permet donc de vous assurer que les valeurs
entrées par l'utilisateur sont des conditions d'interrogation valides.
Lorsque l'utilisateur a entré des conditions d'interrogation non valides, vous pouvez
interrompre une interrogation en générant l'exception intégrée FORM_TRIGGER_FAILURE dans
le déclencheur Pre-Query.
Vous pouvez également appeler SET_BLOCK_PROPERTY pour modifier les clauses
WHERE et ORDER BY du bloc à partir du déclencheur Pre-Query, pour limiter ou ordonner
davantage les enregistrements qui seront extraits par l'interrogation.
Rappel
Le déclencheur Post-Query est activé après l'exécution de l'interrogation, lorsque
FormsRuntime lit des enregistrements dans l'application. Post-Query est activé 1 fois après
l'extraction de chaque enregistrement par l'application, et avant leur affichage dans le bloc.
P.-J. Charrel & J.-M. Thévenin, Université Toulouse 1, Licence Pro
RTICPMO
page 44
Téléchargement