5 Interface entre SQL et un programme
SQL incomplet
Défaut d'impédance (impedance mismatch)
– modèle de données BD
– modèle de données du langage de
programmation
27/02/22 © Robert Godin. Tous droits réservés .
2
Interface programmatique (Call- Level Interface - SQL/CLI)
API spécifique au SGBD
–
e.g. Oracle Call Interface
–
non portable
API normalisée
–
standard de facto ODBC
développé par Microsoft pour le C
–
pilote ODBC pour client/serveur
–
SQL/CLI de SQL:1999 inspirée de ODBC
– JDBC pour Java
SQL enchâssé
(Embedded SQL)
Code SQL dans le source du langage hôte
Syntaxe spéciale
Pré-compilation
– Oracle : pro*C/C
++, pro*COBOL, …, JSQL
Moins portable
– pré-compilateur spécifique au SGBD
– traduit en API du SGBD
SQLJ (partie 0) traduit en API standard JDBC
27/02/22 © Robert Godin. Tous droits réservés .
4
Extension procédurale à SQL (Persistent Stored Modules - SQL/PSM)
SQL +
– structures de contrôle
– procédures, fonctions, packages
– support direct des types SQL pour les variables
– exécution au niveau serveur de BD
Oracle
– PL/SQL
Routine invoquée en SQL
Stockée dans le schéma relationnel
Exécutée au niveau du serveur de BD
Langage
– SQL/PSM
– SQLJ (partie 1) : routines stockées en
Java
27/02/22 © Robert Godin. Tous droits réservés .
6
5.1SQL enchâssé
Syntaxe
Insertion d'une ligne dans la table Client
EXEC SQL opérationSQLEnchâssée END-EXEC {«;» en C}
EXEC SQL INSERT INTO Client
VALUES (100, 'G. Lemoyne-Allaire', '911');
Variables partagées
Déclaration
Utilisation de variables partagées
Vérification d ’exceptions avec SQLSTATE
scanf("%d%s%s",&no,nom,tel);
EXEC SQL INSERT INTO Client VALUES (:no, :nom, :tel);
EXEC SQL BEGIN DECLARE SECTION;
int no;
char nom[15];
char tel[15];
char SQLSTATE[6];
EXEC SQL END DECLARE SECTION;
If(strcmp(SQLSTATE, "xxxxx"))…
27/02/22 © Robert Godin. Tous droits réservés .
8
5.1.1 SELECT à ligne unique
EXEC SQL SELECT nomClient, noTelephone INTO :nom, :tel
FROM Client
WHERE noClient = 10;
printf("Nom: %s Téléphone: %s",nom, tel);
5.1.2 Curseur SQL
void iterateurClient() {
/* Déclaration des variables partagées*/
EXEC SQL BEGIN DECLARE SECTION;
int no;
char nom[15];
char SQLSTATE[6];
EXEC SQL END DECLARE SECTION;
/*Déclaration du curseur*/
EXEC SQL DECLARE curseurClient CURSOR FOR
SELECT noClient, nomClient FROM Client WHERE noClient > 40;
/*Déclenchement de l'opération SQL*/
EXEC SQL OPEN curseurClient;
while(1){
/*Extraction de la ligne suivante*/
EXEC SQL FETCH FROM curseurClient INTO :no, :nom;
if(/* test de fin de table */
!strcmp(SQLSTATE, « 02000 »)) break;
/* traitement utilisant les variables partagées no et nom*/
}
/*Fermeture du curseur*/
EXEC SQL CLOSE curseurClient;
27/02/22 © Robert Godin. Tous droits réservés .
10
5.1.3 Mises à jour par curseur
void iterateurModifieurArticle () {
/* Déclaration des variables partagées*/
EXEC SQL BEGIN DECLARE SECTION;
float prix;
char SQLSTATE[6];
EXEC SQL END DECLARE SECTION;
/*Déclaration du curseur*/
EXEC SQL DECLARE curseurArticle CURSOR FOR SELECT prixUnitaire FROM Article FOR UPDATE;
/*Déclenchement de l'opération SQL*/
EXEC SQL OPEN curseurArticle;
while(1){
/*Extraction de la ligne suivante*/
EXEC SQL FETCH FROM curseurArticle INTO :prix;
if(/* test de fin de table */
!strcmp(SQLSTATE, "02000")) break;
if(prix > 25.0)
EXEC SQL DELETE FROM Article
WHERE CURRENT OF curseurArticle ; Else
EXEC SQL UPDATE Article
SET prixUnitaire = prixUnitaire * 1.1 WHERE CURRENT OF curseurArticle ; }
/*Fermeture du curseur*/
EXEC SQL CLOSE curseurArticle;
};
5.1.4 SQL dynamique
Char chaineSource[…];
printf("Entrez une opération SQL");
scanf("%s", chaineSource);
EXEC SQL PREPARE operationSQL FROM :chaineSource;
EXEC SQL EXECUTE operationSQL ;
27/02/22 © Robert Godin. Tous droits réservés .
12
5.1.5 Connexion (CONNECT)
R é s e a u
L o g i c i e l i n t e r m é d i a i r e
P i l o t e d e
t é l é c o m m u n i c a t i o n P r o g r a m m e d 'a p p l i c a t i o n
L o g i c i e l i n t e r m é d i a i r e
P i l o t e d e
t é l é c o m m u n i c a t i o n S G B D S Q L
C l i e n t S e r v e u r
B D
Suite
Syntaxe du CONNECT
Exemple avec SQL enchâssé en C
Pour passer d ’un connexion à l ’autre
Pour terminer une connexion
CONNECT TO{[ nomServeur [AS nomConnexion]
[USER authorizationID]]|DEFAULT}
EXEC SQL CONNECT TO labunix USER toto ;
SET CONNECTION {[ nomConnexion]|DEFAULT}
DISCONNECT {[nomConnection]|CURRENT|ALL}
27/02/22 © Robert Godin. Tous droits réservés .
14
5.2 Introduction à l'extension procédurale PL/SQL D'Oracle
PROCEDURE pStatutCommande
(leNoCommande Commande.noCommande%TYPE ) IS -- Déclaration de variables
leNoClient Client.noClient%TYPE;
laDateCommande Commande.dateCommande%TYPE ; leNoArticle Article.noArticle%TYPE ; laQuantitéCommandée LigneCommande.quantité%TYPE ; laQuantitéLivrée INTEGER;
laQuantitéEnAttente INTEGER;
-- Déclaration d'un curseur (CURSOR) PL/SQL pour itérer sur les lignes
-- de la commande
CURSOR lignesCommande(unNoCommande Commande.noCommande%TYPE)IS SELECT noArticle, quantité
FROM LigneCommande
WHERE LigneCommande.noCommande = unNoCommande ; BEGIN
DBMS_OUTPUT.PUT_LINE('Commande #:'||TO_CHAR(leNoCommande));
SELECT noClient, dateCommande INTO leNoClient, laDateCommande FROM Commande
WHERE noCommande = leNoCommande;
DBMS_OUTPUT.PUT_LINE('noClient:'||TO_CHAR(leNoClient));
DBMS_OUTPUT.PUT_LINE('dateCommande:'||TO_CHAR(laDateCommande));
OPEN lignesCommande(leNoCommande);
-- Le OPEN ouvre le CURSOR en lui passant les paramètres LOOP
FETCH lignesCommande INTO leNoArticle, laQuantitéCommandée;
-- Le FETCH retourne la ligne suivante EXIT WHEN lignesCommande%NOTFOUND;
-- %NOTFOUND est un attribut du CURSOR qui permet de déterminer -- si le FETCH a atteint la fin de la table
DBMS_OUTPUT.PUT('noArticle :');
DBMS_OUTPUT.PUT(leNoArticle);
DBMS_OUTPUT.PUT(' quantité commandée:');
DBMS_OUTPUT.PUT(laQuantitéCommandée);
-- Chercher la quantité déjà livrée SELECT SUM(quantitéLivrée) INTO laQuantitéLivrée FROM DétailLivraison
WHERE noArticle = leNoArticle AND noCommande = leNoCommande ; IF (laQuantitéLivrée IS NULL) THEN
DBMS_OUTPUT.PUT_LINE(' livraison en attente');
ELSE
laQuantitéEnAttente:= laQuantitéCommandée -laQuantitéLivrée;
IF (laQuantitéEnAttente = 0) THEN
DBMS_OUTPUT.PUT_LINE(' livraison complétée');
ELSE
DBMS_OUTPUT.PUT (' quantité en attente :');
DBMS_OUTPUT.PUT_LINE(laQuantitéEnAttente);
END IF ; END IF ; END LOOP;
-- Le CLOSE ferme le CURSOR CLOSE lignesCommande;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Numéro de commande inexistant');
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'Exception levée par la procédure');
27/02/22 © Robert Godin. Tous droits réservés .
16
Compilation et stockage dans le schéma avec SQL*plus
SQL> CREATE PROCEDURE pStatutCommande
2 (leNoCommande Commande.noCommande%TYPE ) IS 3 4 -- Déclaration de variables
5 leNoClient Client.noClient%TYPE;
6 laDateCommande Commande.dateCommande%TYPE ; 7 leNoArticle Article.noArticle%TYPE ;
8 laQuantitéCommandée LigneCommande.quantité%TYPE ; 9 laQuantitéLivrée INTEGER;
10 laQuantitéEnAttente INTEGER;
11
…
69 EXCEPTION
70 WHEN NO_DATA_FOUND THEN
71 DBMS_OUTPUT.PUT_LINE('Numéro de commande inexistant');
72 WHEN OTHERS THEN
73 RAISE_APPLICATION_ERROR(-20001,'Exception levée par la procédure');
74 END pStatutCommande;
75 /
Procedure created.
Appel de la procédure avec SQL*plus
SQL> SET SERVEROUTPUT ON
SQL> EXECUTE pStatutCommande(2);
Commande #:2
noArticle :40 quantité :2 livraison complétée noArticle :95 quantité :3 quantité en attente :2 PL/SQL procedure successfully completed.
SQL> EXECUTE pStatutCommande(5);
Commande #:5
noArticle :10 quantité :5 livraison en attente noArticle :20 quantité :5 livraison en attente noArticle :70 quantité :3 quantité en attente :1 PL/SQL procedure successfully completed.
SQL> EXECUTE pStatutCommande(10);
Commande #:10
Numéro de commande inexistant
PL/SQL procedure successfully completed.
27/02/22 © Robert Godin. Tous droits réservés .
18
5.2.1 Bloc PL/SQL
[DECLARE
déclaration [déclaration] ...]
BEGIN
séquenceEnoncés [EXCEPTION
exception_énoncé [exception_énoncé] ...]
END
Exécution d ’un bloc sous SQL*plus
SQL> DECLARE
2 laQuantitéEnStock Article.quantitéEnStock%TYPE;
3 BEGIN
4 SELECT quantitéEnStock INTO laQuantitéEnStock 5 FROM Article
6 WHERE noArticle = 10;
7 IF laQuantitéEnStock = 0 THEN
8 DBMS_OUTPUT.PUT_LINE('L article est en rupture de stock');
9 ELSE
10 DBMS_OUTPUT.PUT('Quantité en stock :');
11 DBMS_OUTPUT.PUT_LINE(laQuantitéEnStock);
12 END IF;
13 EXCEPTION
14 WHEN NO_DATA_FOUND THEN
15 DBMS_OUTPUT.PUT_LINE('Numéro d article inexistant');
16 WHEN OTHERS THEN
17 RAISE_APPLICATION_ERROR(-20001,'Erreur soulevée par le SELECT');
18 END;
19 /
Quantité en stock :20
PL/SQL procedure successfully completed.
27/02/22 © Robert Godin. Tous droits réservés .
20
5.2.2 Déclaration de variables PL/SQL (DECLARE)
leNoClient Client.noClient%TYPE;
leNoClient INTEGER;
5.2.3 Transfert d'une valeur de colonne d'un SELECT dans une variable (clause INTO)
SELECT noClient, dateCommande
INTO leNoClient, laDateCommande FROM Commande
WHERE noCommande = leNoCommande;
27/02/22 © Robert Godin. Tous droits réservés .
22
5.2.4 Affectation en PL/SQL
laQuantitéEnAttente:= laQuantitéCommandée -laQuantitéLivrée;
5.2.5 Structure de contrôle IF
IF (laQuantitéLivrée IS NULL) THEN
DBMS_OUTPUT.PUT_LINE(' livraison en attente');
ELSE
laQuantitéEnAttente:= laQuantitéCommandée -laQuantitéLivrée;
IF (laQuantitéEnAttente = 0) THEN
DBMS_OUTPUT.PUT_LINE(' livraison complétée');
ELSE
DBMS_OUTPUT.PUT (' quantité en attente :');
DBMS_OUTPUT.PUT_LINE(laQuantitéEnAttente);
END IF ; END IF ;
27/02/22 © Robert Godin. Tous droits réservés .
24
5.2.6 Boucles (LOOP, FOR, WHILE)
LOOP
séquenceEnoncés
END LOOP ;
FOR indice IN [REVERSE] valeurInitiale..valeurFinale LOOP
séquenceEnoncés
END LOOP ;
WHILE condition LOOP
séquenceEnoncés
END LOOP ;
5.2.7 Traitement d'exception (EXCEPTION, RAISE)
Déclarer
Soulever
Attraper
nomException EXCEPTION;
RAISE nomException
WHEN nomException THEN
séquenceÉnoncés
27/02/22 © Robert Godin. Tous droits réservés .
26
5.2.8 Curseur PL/SQL (CURSOR)
CURSOR lignesCommande(unNoCommande Commande.noCommande%TYPE)IS SELECT noArticle, quantité
FROM LigneCommande
WHERE LigneCommande.noCommande = unNoCommande ;
OPEN lignesCommande(leNoCommande);
-- Le OPEN ouvre le CURSOR en lui passant les paramètres
LOOP FETCH lignesCommande INTO leNoArticle, laQuantitéCommandée;
-- Le FETCH retourne la ligne suivante EXIT WHEN lignesCommande%NOTFOUND;
-- %NOTFOUND est un attribut du CURSOR qui permet de déterminer -- si le FETCH a atteint la fin de la table
…
END LOOP;
-- Le CLOSE ferme le CURSOR CLOSE lignesCommande;
Boucle FOR pour curseur PL/SQL
FOR uneLigne IN lignesCommande(leNoCommande) LOOP DBMS_OUTPUT.PUT('noArticle :');
DBMS_OUTPUT.PUT(uneLigne.noArticle);
DBMS_OUTPUT.PUT(' quantité commandée:');
DBMS_OUTPUT.PUT(uneLigne.quantité);
-- Chercher la quantité déjà livrée SELECT SUM(quantitéLivrée) INTO laQuantitéLivrée FROM DétailLivraison
WHERE noArticle = uneLigne.noArticle AND noCommande = leNoCommande ;
IF (laQuantitéLivrée IS NULL) THEN
DBMS_OUTPUT.PUT_LINE(' livraison en attente');
ELSE
laQuantitéEnAttente:= uneLigne.quantité -laQuantitéLivrée;
IF (laQuantitéEnAttente = 0) THEN
DBMS_OUTPUT.PUT_LINE(' livraison complétée');
ELSE
DBMS_OUTPUT.PUT (' quantité en attente :');
DBMS_OUTPUT.PUT_LINE(laQuantitéEnAttente);
END IF ; END IF ;
END LOOP;
27/02/22 © Robert Godin. Tous droits réservés .
28
5.2.9 Procédures et fonctions PL/SQL stockées
SQL> CREATE FUNCTION fQuantitéEnStock
2 (unNoArticle Article.noArticle%TYPE) 3 RETURN Article.quantitéEnStock%TYPE IS 4
5 uneQuantitéEnStock Article.quantitéEnStock%TYPE;
6 BEGIN
7 SELECT quantitéEnStock 8 INTO uneQuantitéEnStock 9 FROM Article
10 WHERE noArticle = unNoArticle;
11 RETURN uneQuantitéEnStock;
12 END fQuantitéEnStock;
13 14 /
Function created.
SQL> select fQuantitéEnStock(10) from dual;
FQUANTITÉENSTOCK(10) --- 10
Procédure stockée
SQL> CREATE PROCEDURE pModifierQuantitéEnStock 2 (unNoArticle Article.noArticle%TYPE,
3 nouvelleQuantitéEnStock Article.quantitéEnStock%TYPE) IS 4 BEGIN
5 UPDATE Article
6 SET quantitéEnStock = nouvelleQuantitéEnStock 7 WHERE noArticle = unNoArticle;
8 END pModifierQuantitéEnStock;
9 /
Procedure created.
SQL> EXECUTE pModifierQuantitéEnStock(10,20);
PL/SQL procedure successfully completed.
SQL> SELECT * FROM Article WHERE noArticle = 10;
NOARTICLE DESCRIPTION PRIXUNITAIRE QUANTITÉENSTOCK --- --- --- --- 10 Cèdre en boule 10,99 20
27/02/22 © Robert Godin. Tous droits réservés .
30
5.2.9.1 PRIVILÈGES DANS UNE ROUTINE STOCKÉE
Privilèges de l'appelant (invoker rights)
Privilèges de la routine (definer rights)
– privilèges du créateur
– par défaut
CREATE PROCEDURE pModifierQuantitéEnStock
(unNoArticle Article.noArticle%TYPE,
nouvelleQuantitéEnStock Article.quantitéEnStock%TYPE) AUTHID CURRENT_USER IS
BEGIN
UPDATE Article
SET quantitéEnStock = nouvelleQuantitéEnStock WHERE noArticle = unNoArticle;
END pModifierQuantitéEnStock;
Exemple privilège de l’appelant
27/02/22 © Robert Godin. Tous droits réservés .
32
Exemple privilège du créateur
Exemple privilège du créateur
(suite)
27/02/22 © Robert Godin. Tous droits réservés .
34
5.2.10 Package PL/SQL
CREATE PACKAGE nomPaquetage AS
listeDesSignaturesDesFonctions&Procédures END nomPaquetage ;
CREATE PACKAGE BODY nomPaquetage AS délaration [déclaration]…
BEGIN
séquenceÉnoncésInitialisation END nomPaquetage;
nomPaquetage.nomObjet
5.2.11 Déboguage du code PL/SQL
SHOW ERRORS sous SQL*plus
Package DBMS_OUTPUT
Table USER_SOURCE dans la métabase
SQL> SELECT text 2 FROM USER_SOURCE
3 WHERE name = 'FQUANTITÉENSTOCK' AND type = 'FUNCTION' 4 ORDER BY line;
TEXT
--- ---
FUNCTION fQuantitéEnStock
(unNoArticle Article.noArticle%TYPE) RETURN Article. quantitéEnStock%TYPE IS
uneQuantitéEnStock Article.quantitéEnStock%TYPE;
BEGIN
SELECT quantitéEnStock INTO uneQuantitéEnStock FROM Article
WHERE noArticle = unNoArticle;
RETURN uneQuantitéEnStock;
TEXT
--- ---
END fQuantitéEnStock;
27/02/22 © Robert Godin. Tous droits réservés .
36
5.3 JDBC
API standard pour JAVA
http://java.sun.com/products/jdbc/
Ensemble de classes
Besoin d ’installer un pilote JDBC
dans l ’environnement JAVA
5.3.1 Architecture pour les pilotes JDBC
A P I J D B C T y p e 1
P a s s e r e l l e J D B C - O D B C
A p p l i c a t i o n J a v a ( i m p o r t j a v a . s q l . * )
T y p e 2 P a r t i e J a v a P i l o t e O D B C
( e . g . p i l o t e O D B C O r a c l e )
T y p e 3 P i l o t e J D B C
g é n é r i q u e T y p e 4
P i l o t e J D B C t o u t J a v a
( e . g . O r a c l e t h i n )
S e r v e u r J D B C A P I c l i e n t d u
S G B D ( e . g . o c i j d b c 8 . d l l
p o u r O r a c l e )
S e r v e u r d e B D
T y p e ? O r a c l e s e r v e r -
s i d e t h i n e t i n t e r n a l d r i v e r
27/02/22 © Robert Godin. Tous droits réservés .
38
5.3.2 Chargement d'un pilote JDBC
(DriverManager) et établissement d'une connexion (Connection)
Charger les pilotes JDBC d ’ Oracle
– N.B. La librairie des pilotes Oracle doit être accessible à la machine virtuelle Java. Voir http://www.info.uqam.ca/~godin/livres.html
– Avec JDK 1.1 (problème avec bloc statique) utiliser :
DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
Établir une connexion avec le pilote OCI8 pour un serveur Oracle local
– OCI8 est un pilote de type 2
Voir aussi interface javax.sql.DataSource (chap. 15)
– package optionnel de JDBC 2
– meilleure transparence, plus grande versatilité
– maintenant préférée à DriverManager
– obligatoire avec J2EE
Class.forName ("oracle.jdbc.driver.OracleDriver");
Connection uneConnection =
DriverManager.getConnection
("jdbc:oracle:oci8:@", "toto", "secret");
5.3.3 Création d ’un
énoncé SQL (Statement)
Statement
executeQuery(arg0 : String) : ResultSet executeUpdate(arg0 : String) : int close() : void
getMaxFieldSize() : int
setMaxFieldSize(arg0 : int) : void getMaxRows() : int
setMaxRows(arg0 : int) : void
setEscapeProcessing(arg0 : boolean) : void getQueryTimeout() : int
setQueryTimeout(arg0 : int) : void cancel() : void
getW arnings() : SQLWarning clearWarnings() : void
setCursorName(arg0 : String) : void execute(arg0 : String) : boolean getResultSet() : ResultSet getUpdateCount() : int getMoreResults() : boolean setFetchDirection(arg0 : int) : void getFetchDirection() : int
setFetchSize(arg0 : int) : void getFetchSize() : int
getResultSetConcurrency() : int getResultSetType() : int addBatch(arg0 : String) : void clearBatch() : void
executeBatch() : int[]
<<Interface>>
Statement unEnoncéSQL = uneConnection.createStatement ();
27/02/22 © Robert Godin. Tous droits réservés .
40
5.3.4 Exécution d'une opération de mise à jour (INSERT, DELETE, UPDATE)
import java.sql.*;
class ClientInsertJDBC {
public static void main (String args [])
throws SQLException, ClassNotFoundException, java.io.IOException {
// Charger le pilote JDBC d'Oracle
Class.forName ("oracle.jdbc.driver.OracleDriver");
// Connexion à une BD
Connection uneConnection =
DriverManager.getConnection ("jdbc:oracle:oci8:@", "godin", "oracle");
// Création d'un énoncé associé à la Connection
Statement unEnoncéSQL = uneConnection.createStatement ();
// Insertion d'une ligne dans la table Client int n = unEnoncéSQL.executeUpdate
("INSERT INTO CLIENT " +
"VALUES (100, 'G. Lemoyne-Allaire', '911')");
System.out.println ("Nombre de lignes inserees:" + n);
// Fermeture de l'énoncé et de la connexion unEnoncéSQL.close();
uneConnection.close();
} }
5.3.6 Exécution d'un SELECT (ResultSet)
… Début analogue à l'exemple précédent
// Création d'un énoncé associé à la Connexion
Statement unEnoncéSQL = uneConnection.createStatement();
// Exécution d'un SELECT
ResultSet résultatSelect = unEnoncéSQL.executeQuery ("SELECT noClient, nomClient "+
"FROM CLIENT " +
"WHERE noClient > 40");
// Itérer sur les lignes du résultat du SELECT et extraire les valeurs // des colonnes dans des variables JAVA
while (résultatSelect.next ()){
int noClient = résultatSelect.getInt ("noClient");
String nomClient = résultatSelect.getString ("nomClient");
System.out.println ("Numéro du client:" + noClient);
System.out.println ("Nom du client:" + nomClient);
} } }
27/02/22 © Robert Godin. Tous droits réservés .
42
5.3.7 ResultSet défilable (scrollable), modifiable (updatable), sensible
(sensitive) - JDBC2
//Positionnement à la première ligne du ResultSet résultatSelect.first();
// Positionnement à la dernière ligne du ResultSet résultatSelect.last();
// Positionnement à la troisième ligne résultatSelect.absolute(3);
// Positionnement relatif (avancer de 2 lignes) résultatSelect.relative(2);
// Positionnement à la ligne précédente résultatSelect.previous();
// Création d'un énoncé avec ResultSet défilable (srollable) Statement unEnoncéSQL =
uneConnection.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
// Exécution d'un SELECT
ResultSet résultatSelect = unEnoncéSQL.executeQuery ("SELECT noClient, nomClient FROM CLIENT");
ResultSet modifiable (updatable)
// Création d'un énoncé avec ResultSet défilable (srollable) Statement unEnoncéSQL =
uneConnection.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
// Exécution d'un SELECT
ResultSet résultatSelect = unEnoncéSQL.executeQuery ("SELECT noClient, nomClient, noTéléphone FROM CLIENT");
// Exemples de mises-à-jour par ResultSet updatable //Positionnement à la première ligne du ResultSet résultatSelect.first();
// Mise-à-jour de la colonne noTéléphone de la ligne courante résultatSelect.updateString("noTéléphone","(111)111-1111");
// Effectuer le UPDATE résultatSelect.updateRow();
// Positionnement à la dernière ligne de la table résultatSelect.last();
// Supprimer la ligne courante résultatSelect.deleteRow();
// Insertion d'une nouvelle ligne résultatSelect.moveToInsertRow();
résultatSelect.updateInt("noClient", 100);
résultatSelect.updateString("nomClient", "G. Lemoyne-Allaire");
résultatSelect.updateString("noTéléphone", "911");
mises à jour susceptibles
d ’être vues pas le ResultSet
27/02/22 © Robert Godin. Tous droits réservés .
44
5.3.8 Support des types SQL:1999 sous JDBC 2
CREATE TABLE tableBlob (
idBlob INTEGER PRIMARY KEY, imageBLOB)
// Chercher le BLOB locator
ResultSet unResultSet = unEnoncéSQL.executeQuery ("SELECT * FROM tableBlob WHERE idBlob = 1");
if (unResultSet.next()){
int idBlob = unResultSet.getInt(1);
Blob unBlob = unResultSet.getBlob(2);
// Chercher la taille du BLOB et l'afficher int taille = (int)unBlob.length();
System.out.println("Taille du BLOB" + taille);
// Lire le BLOB dans un tableau d'octets byte octets[] = unBlob.getBytes(1, taille);
// Créer un fichier contenant les octets lus FileOutputStream unFichier =
new FileOutputStream("C:/forte4j/Development/ExemplesJDBC/CopieCoq1.gif");
unFichier.write(octets);
unFichier.close();
5.3.9 Exécution en lot (batch) sous JDBC 2
// Création d'un PreparedStatement associé à la Connection PreparedStatement unEnoncéSQL = uneConnection.prepareStatement ("INSERT INTO Client VALUES(?,?,?)");
// Ajout d'un INSERT dans le lot unEnoncéSQL.setInt(1,90);
unEnoncéSQL.setString(2,"Edgar Degas");
unEnoncéSQL.setString(3,"(222)222-2222");
unEnoncéSQL.addBatch();
// Ajout d'un autre INSERT dans le lot unEnoncéSQL.setInt(1,100);
unEnoncéSQL.setString(2,"Claude Monet");
unEnoncéSQL.setString(3,"(111)111-1111");
unEnoncéSQL.addBatch();
// Exécution du lot en un appel
int [] résultats = unEnoncéSQL.executeBatch();
27/02/22 © Robert Godin. Tous droits réservés .
46
5.3.10Gestion des transactions
Par défaut : auto-commit
Pour modifier
Pour un commit explicite :
Pour un rollback :
uneConnection.setAutoCommit(false);
uneConnection.commit();
uneConnection.rollback();
Suite
Changer le niveau d ’isolation de défaut
Transactions réparties JDBC 2
– Java Transaction API (JTA)
interface UserTransaction
–
begin(), commit() – voir chap. 15
uneConnection.setTransactionIsolationLevel(unNiveau) ;
27/02/22 © Robert Godin. Tous droits réservés .
48
5.3.11Gestion des exceptions
try
{Class.forName ("oracle.jdbc.driver.OracleDriver");
}
catch(ClassNotFoundException e)
{System.err.println(" ClassNotFoundException:" + e.getMessage());
}
Mécanisme try/catch de JAVA
5.3.12 Utilisation de JDBC dans une applette Java
Déploiement avec le pilote thin
Contrainte de carré de sable
Exemple
– http://www.labunix.uqam.ca/~r11340/ExemplesAppletJDBC/PageAppletJDB C.html
Exemple d ’applette avec pilote JDBC type 4 (thin)
import java.awt.*;
import java.applet.*;
import java.sql.*;
public class AppletJDBC extends Applet { public void paint (Graphics g)
//N.B. On ne peut lancer (throw) des exceptions non déclarées dans le paint() de Applet...
//Pour simplifier l'exemple, il n'y a qu'un seul try pour tous les appels à JDBC {
try{
// NB Charger le pilote JDBC d'Oracle avec DriverManager pour Java 1.1 sous Explorer 5 DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
// Connection à une BD à distance avec un pilote thin Connection uneConnection =
DriverManager.getConnection ("jdbc:oracle:thin:@arabica.cafe.uqam.ca:1521:o8db", "r11340", "motDePasse");
// Création d'un énoncé associé à la Connection
Statement unEnoncéSQL = uneConnection.createStatement ();
// Exécution d'un SELECT
ResultSet résultatSelect = unEnoncéSQL.executeQuery ("SELECT noClient, nomClient "+
"FROM CLIENT " +
"WHERE noClient > 40");
// Itérer sur les lignes du résultat du SELECT et extraire les valeurs // des colonnes dans des variables JAVA
g.drawString("Résultat du SELECT :", 5,15);
g.drawString("Numéro", 5,30); g.drawString("Nom", 100,30);
int i = 1;
while (résultatSelect.next ()){
int noClient = résultatSelect.getInt ("noClient");
String nomClient = résultatSelect.getString ("nomClient");
g.drawString(""+noClient,5,i*15+30);
g.drawString(""+nomClient,100,i*15+30);
i = i+1;
}
// Fermeture de l'énoncé et de la connexion unEnoncéSQL.close();
uneConnection.close();
}
catch(Exception e){System.err.println(" Exception:" + e.getMessage());}
} }
5.3.13 Compilation et exécutions multiples avec la classe PreparedStatement
Statement unEnoncéSQL = uneConnection.createStatement();
ResultSet résultatSelect = unEnoncéSQL.executeQuery ("SELECT noClient, nomClient "+
"FROM CLIENT " +
"WHERE noClient > 40");
PreparedStatement unEnoncéSQL = uneConnection.prepareStatement ("SELECT noClient, nomClient "+
"FROM CLIENT " +
"WHERE noClient > 40");
ResultSet résultatSelect = unEnoncéSQL.executeQuery();
PreparedStatement unEnoncéSQL = uneConnection.prepareStatement ("SELECT noClient, nomClient "+
"FROM CLIENT " +
"WHERE noClient > ?");
unEnoncéSQL.setInt(1,40);
ResultSet résultatSelect = unEnoncéSQL.executeQuery();
Compilation + exécution combinée au executeQuery
Compilation au prepareStatement
Compilation avec paramètres
CallableStatement PreparedStatementStatement
27/02/22 © Robert Godin. Tous droits réservés .
52
5.3.14 Exécution d'une procédure ou fonction stockée (CallableStatement)
// Création d'un appel de fonction associé à la Connection CallableStatement unCall =
uneConnection.prepareCall("{ ? = call fQuantitéEnStock(?)}");
// Spécification du paramètre d'entrée unCall.setInt(2,10);
// Inscription de la sortie
unCall.registerOutParameter(1, java.sql.Types.INTEGER);
// Exécution de l'appel unCall.execute();
// Récupération de la sortie
int laQuantite = unCall.getInt(1);
System.out.println("Quantité en stock :"+laQuantite);
};
unCall.close();
uneConnection.close();
Appel de procédure stockée
CallableStatement unCall =
uneConnection.prepareCall("{call pModifierQuantitéEnStock(?,?)}");
// Spécification des paramètres d'entrée unCall.setInt(1,10);
unCall.setInt(2,20);
// Exécution de l'appel unCall.execute();
unCall.close();
uneConnection.close();
27/02/22 © Robert Godin. Tous droits réservés .
54
5.3.15 Accès aux méta- données (MetaData)
ResultSet résultatSelect = unEnoncéSQL.executeQuery ("SELECT noClient, nomClient "+
"FROM CLIENT " +
"WHERE noClient > 40");
// Consultation de quelques méta-données du ResultSetMetaData ResultSetMetaData unRSMD = résultatSelect.getMetaData();
int nombreColonnes = unRSMD.getColumnCount();
System.out.println("Le résultat du SELECT contient "+nombreColonnes+" colonnes");
for (int indice = 1; indice <= nombreColonnes; indice++){
System.out.println("La colonne "+indice+
" qui se nomme "+unRSMD.getColumnName(indice)+
" est de type "+unRSMD.getColumnTypeName(indice));
Le résultat du SELECT contient 2 colonnes
La colonne 1 qui se nomme NOCLIENT est de type NUMBER
La colonne 2 qui se nomme NOMCLIENT est de type VARCHAR2
DatabaseMetaData
// Consultation de quelques méta-données de la BD DatabaseMetaData unDBM = uneConnection.getMetaData();
System.out.println("Nom du SGBD :"+unDBM.getDatabaseProductName());
System.out.println("Version du SGBD :"+unDBM.getDatabaseProductVersion());
System.out.println("Niveau d'isolation par défaut :"+unDBM.getDefaultTransactionIsolation());
System.out.println("Support du niveau entrée de SQL2 :"+unDBM.supportsANSI92EntryLevelSQL());
System.out.println("Nom du pilote JDBC :"+unDBM.getDriverName());
Nom du SGBD :Oracle
Version du SGBD :Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option
JServer Release 8.1.7.0.0 - Production Niveau d'isolation par défaut :2
Support du niveau entrée de SQL2 :true
Nom du pilote JDBC :Oracle JDBC driver
27/02/22 © Robert Godin. Tous droits réservés .
56
5.4 SQLJ
Partie 0 de SQLJ
– SQL enchâssée en Java
Partie 1 de SQLJ
– Routines stockées
Partie 2 de SQLJ
– Utilisation de classes Java en tant
que types SQL
5.4.1 SQL enchâssé en Java (SQLJ : partie 0)
Ne vise que le statique
Traduit en JDBC
Peut combiner SQLJ et JDBC
#sql "{" énoncéSQL "}" ;
#sql {INSERT INTO CLIENT VALUES (100, 'G. Lemoyne-Allaire', '911')};
#sql {DELETE FROM CLIENT WHERE noClient = 10};
27/02/22 © Robert Godin. Tous droits réservés .
58
Variables partagées
Int no;
String nom;
String tel;
#sql {INSERT INTO CLIENT VALUES (:no, :nom, :tel)};
5.4.1.1 CONTEXTE DE CONNEXION
//Exemple d'insertion d'un Client avec SQLJ package ExemplesSQLJ;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
import java.sql.*;
public class ClientInsertSQLJ{
public static void main (String args [])
throws SQLException, ClassNotFoundException, java.io.IOException { // Charger le pilote JDBC d'Oracle
Class.forName ("oracle.jdbc.driver.OracleDriver");
// Création du contexte de connexion de défaut avec autocommit (true) DefaultContext unContexte = new DefaultContext
("jdbc:oracle:thin:@localhost:1521:ora817i", "godin", "oracle", true);
DefaultContext.setDefaultContext(unContexte);
// Insertion en utilisant le contexte de défaut
#sql {INSERT INTO CLIENT VALUES (100, 'G. Lemoyne-Allaire', '911')};
// Fermeture du contexte de connexion unContexte.close();
System.out.println("Insertion réussie !");
}
Contexte implicite
27/02/22 © Robert Godin. Tous droits réservés .
60
Contexte explicite
// Déclaration de la classe du contexte explicite
#sql context Contexte;
public class ContexteExpliciteSQLJ{
public static void main (String args [])
throws SQLException, ClassNotFoundException, java.io.IOException {
// Charger le pilote JDBC d'Oracle
Class.forName ("oracle.jdbc.driver.OracleDriver");
// Création du contexte de connexion avec autocommit (true) Contexte unContexte = new Contexte
("jdbc:oracle:thin:@localhost:1521:ora817i", "godin", "oracle", true);
// Insertion en utilisant le contexte explicite unContexte
#sql [unContexte] {INSERT INTO CLIENT VALUES (100, 'G. Lemoyne-Allaire', '911')};
Commit explicite
// Création du contexte de connexion de défaut sans autocommit (false) DefaultContext unContexte = new DefaultContext
("jdbc:oracle:thin:@localhost:1521:ora817i", "godin", "oracle", false);
DefaultContext.setDefaultContext(unContexte);
// Insertion en utilisant le contexte de défaut
#sql {INSERT INTO CLIENT VALUES (100, 'G. Lemoyne-Allaire', '911')};
// Confirmer la transaction #sql {COMMIT};
27/02/22 © Robert Godin. Tous droits réservés .
62
5.4.1.3 SELECT QUI RETOURNE UN SINGLETON (CLAUSE INTO)
String nom;
String tel;
// Utilisation de la clause INTO
#sql { SELECT nomClient, noTéléphone INTO :nom, :tel
FROM Client WHERE noClient = 10};
5.4.1.4 ITÉRATEUR DE RÉSULTAT SQLJ
// Définition de la classe IteratorClient avec liaison par nom #sql iterator IteratorClient(int noClient, String nomClient);
// Création d'un objet itérateur IteratorClient unIteratorClient;
// Liaison de l'énoncé SELECT de l'itérateur #sql unIteratorClient =
{ SELECT noClient, nomClient
FROM Client WHERE noClient > 40};
// Accès au résultat du SELECT par itération sur les lignes while (unIteratorClient.next()){
System.out.println("Numéro du client : " + unIteratorClient.noClient());
System.out.println("Nom du client : " + unIteratorClient.nomClient());
}
// Fermer l'itérateur unIteratorClient.close();
27/02/22 © Robert Godin. Tous droits réservés .
64
Mise-à-jour par itérateur
// Définition de la classe IteratorClient avec liaison par nom #sql iterator IteratorClient implements sqlj.runtime.ForUpdate (int noClient, String nomClient);
// Création d'un objet itérateur IteratorClient unIteratorClient;
// Liaison de l'énoncé SELECT de l'itérateur #sql unIteratorClient =
{ SELECT noClient, nomClient, noTéléphone FROM Client WHERE noClient > 40};
// Accès au résultat du SELECT par itération sur les lignes while (unIteratorClient.next()){
if (unIteratorClient.noClient()== 60){
#sql {UPDATE Client
SET noTéléphone = '(111)111-1111' WHERE CURRENT of :unIteratorClient};
} else if (unIteratorClient.noClient()== 80){
#sql {DELETE FROM Client
WHERE CURRENT of :unIteratorClient};
}
5.4.1.5 APPEL DE ROUTINES STOCKÉES EN SQLJ
int laQuantite = 0;
int noArticle = 10 ;
// Appel de la fonction stockée
#sql laQuantite = {VALUES (fQuantitéEnStock(:noArticle))};
// Fermeture du contexte de connexion unContexte.close();
System.out.println("Quantité en stock :" + laQuantite); }
// Appel de la procédure stockée
#sql {CALL pModifierQuantitéEnStock(:in noArticle,:in laQuantite)};
27/02/22 © Robert Godin. Tous droits réservés .
66 Connection uneConnection =
DriverManager.getConnection
("jdbc:oracle:thin:@localhost:1521:ora817i", "godin", "oracle");
Contexte unContexte = new Contexte(uneConnection);
5.4.1.6 INTEROPÉRABILITÉ AVEC JDBC
Création d'un contexte SQLJ à partir d'une Connection JDBC
Extraction de la Connection d'un contexte
Conversion d'un itérateur en un ResultSet JDBC
Conversion d'un ResultSet JDBC en un iterateur SQLJ
Connection uneConnection = unContexte.getConnection();
ResultSet résultatSelect = unIteratorClient.getResultSet();
#sql unIteratorClient = {CAST :unResultSet};
5.4.1.7 PROCESSUS DE TRADUCTION SQLJ
S c h é m a S Q L
N o m C l a s s e . S Q L J
T r a d u c t e u r S Q L J ( p a r t i e 0 )
N o m C l a s s e . J a v a
N o m C l a s s e _ S J P r o f i l e 0 . s e r N o m C l a s s e _ S J P r o f i l e 1 . s e r
. . .
C o m p i l a t e u r J a v a
N o m C l a s s e . c l a s s C l a s s e s d e l '
e n v i r o n n e m e n t d 'e x é c u t i o n S Q L J
F i c h i e r d e d é p l o i e m e n t ( . j a r )
A d a p t a t i o n s p é c i f i q u e à u n
27/02/22 © Robert Godin. Tous droits réservés .
68
5.4.2 SQLJ partie 1: routines stokées en Java
import java.sql.*;
import java.io.*;
public class RoutineServeur extends Object {
public static int getQuantiteEnStock (int noArticle) throws SQLException {
// Retourne -1 si l'article n'existe pas PreparedStatement unEnoncéSQL = null;
int quantitéEnStock = -1;
try {
Connection uneConnection =
DriverManager.getConnection("jdbc:default:connection:");
unEnoncéSQL = uneConnection.prepareStatement
("SELECT quantitéEnStock FROM Article WHERE noArticle = ? ");
unEnoncéSQL.setInt(1,noArticle);
ResultSet résultatSelect = unEnoncéSQL.executeQuery();
if (résultatSelect.next ()){
quantitéEnStock = résultatSelect.getInt(1);
} }
catch (SQLException e) {System.err.println(e.getMessage());}
finally{unEnoncéSQL.close();}
return quantitéEnStock;
}
public static void setQuantiteEnStock (int noArticle, int quantitéEnStock) throws SQLException {
PreparedStatement unEnoncéSQL = null;
try {
Connection uneConnection =
DriverManager.getConnection("jdbc:default:connection:");
unEnoncéSQL = uneConnection.prepareStatement
("UPDATE Article SET quantitéEnStock = ? WHERE noArticle = ? ");
unEnoncéSQL.setInt(1,quantitéEnStock);
unEnoncéSQL.setInt(2,noArticle);
unEnoncéSQL.executeUpdate();
}
catch (SQLException e) {System.err.println(e.getMessage());}
finally{unEnoncéSQL.close();}
} }
Déploiement Oracle
Charger le code dans un schéma
Publier sous forme de routine stockée :
Appeler la fonction en SQL
loadjava -user godin/oracle RoutineServeur.class
SQL> CREATE OR REPLACE FUNCTION getQuantiteEnStock(noArticle NUMBER) 2 RETURN NUMBER
3 AS LANGUAGE JAVA
4 NAME ' RoutineServeur.getQuantiteEnStock (int) return int';
5 /
Function created.
SQL> select getQuantiteEnStock(10) from dual;
GETQUANTITEENSTOCK(10) --- 20