DEVELOPPEMENT CLIENT-SERVEUR EN JAVA

    

 

1         Présentation

 

JDBC (Java DataBase Connectivity) est voisin d’ODBC. L’API JDBC est l’ensemble des classes et interfaces permettant de travailler (connexion à la base, exécution de requêtes et de procédures et fonctions stockées, exploitation des résultats renvoyés…) sur des bases de données relationnelles dans des programmes Java.

Les classes et interfaces de l'API JDBC figurent dans le package java.sql.

 

Il est indispensable d’importer l’API JDBC dans tout programme se servant de la technologie JDBC ; pour cela, il faut écrire :

 

import java.sql.* ;

 

Il faut dans un premier temps établir une connexion à la base puis effectuer les actions nécessaires sur les données.

 

 

Remarque concernant le package java.sql :

En fait, le paquetage java.sql contient principalement des interfaces. Pour être utilisées, ces interfaces devront être mises en œuvre (implémentées) dans la classe correspondant au pilote permettant l’accès à la base de données.

 

 

2         Connexion à la base de données

 

2.1      Types de pilotes

 

Il existe un ou plusieurs drivers JDBC spécifiques pour chaque SGBD (système de gestion de bases de données : Access, Oracle, Informix, ...).

Les pilotes JDBC sont classés en 2 familles :

·      La famille de pilotes non 100% Java :ce sont des pilotes qui utilisent une partie écrite dans un autre langage que Java (souvent en C). Ces pilotes doivent être installés sur la station cliente. Dans cette 1ère famille, on distingue :

·      La famille de pilotes 100% Java :ils interrogent le gestionnaire de base de données avec du code uniquement écrit en Java. Il n’y a aucune installation à effectuer sur le client ce qui permet d’interroger la base de données dans une applet. Dans cette 2ème famille, on distingue :

 

2.1.1       Pilotes non 100% Java

 

2.1.1.1  Connexion via jdbc-odbc (type 1)

a) Chargement du pilote

Il faut d’abord charger le pilote pour permettre la connexion à la base et le travail sur les données, cela se fait grâce à un appel à la méthode forName (de la classe Class) à laquelle on transmet le nom de la classe du pilote. Le pilote est alors enregistré auprès du DriverManager (gestionnaire de pilotes) :

try 
{
               Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch(ClassNotFoundException e) 
{
               System.out.println("JDBC driver not found.");
}

 

Quel que soit le pilote qu’on essaie de charger, c’est l’exception ClassNotFoundException qui est levée (c’est à dire déclenchée) en cas d’échec. Il est obligatoire d’intercepter cette exception ou bien de la renvoyer.

b) Connexion proprement dite

Nous allons partir de l'exemple d'une base des voyages sous Access. Précisons bien que le SGBD Access ne permet pas de travailler en vrai mode client-serveur mais la façon de procéder est la même, la différence résidera bien sûr dans les performances.

Cette base contient les tables suivantes :

T_Voyages(NoVoyage, LibVoyage, DateDebut, Duree, NbInscrits,NoDestination)
T_Destinations(NoDestination, LibDestination)

 

La connexion à la base se fait via le DSN(data source name) BDVoyage qui s’appuie sur le pilote Microsoft Access Driver.

 

Pour se connecter à une base, il est nécessaire de déclarer une référence de type Connection et de créer la connexion par un appel à la méthode getConnection de la classe Drivermanager en indiquant la base de données à l'aide d'une chaîne de connexion.

 

String cochaine = "jdbc:odbc:Bdvoyage";                             // chaîne de connexion avec  DSN

Connection conn = DriverManager.getConnection(cochaine);     // on effectue la connexion

                                               //grâce à la méthode getConnection de la classe DriverManager                                                                                     

 

Syntaxe générale pour la connexion :

Connection objetConnection = DriverManager.getConnection(ChaîneDeConnexion,"NomUtilisateur","MotDePasse");

 

Remarques :

·         l’accès à une base Oracle dans un programme Java peut être effectué via un DSN ODBC (on choisira bien sûr un pilote pour Oracle lors de la création du DSN). Mais dans le cas d’Oracle, il faudra impérativement préciser le nom d’utilisateur et le mot de passe dans l’instruction de connexion. Sur le serveur, il faudra que le listener Oracle ait été lancé. Sur le client, il faudra avoir installé Net8 et le driver odbc pour Oracle devra bien sûr être présent.

·         En cas d’échec de la connexion, c’est l’exception SQLException qui est levée. Il est obligatoire d’intercepter cette exception ou bien de la renvoyer.

 

2.1.1.2  Connexion via une API native à installer sur le client (type 2)

 

Nous illustrons ce paragraphe avec Oracle.

Il faudra installer Oracle OCI Driver sur le serveur et installer Net8 sur le client.

Il faut que le classpath contienne le chemin de la bibliothèque des drivers Oracle donc le classpath sera :

.;\\NomServeur\NomRépPartagé\Ora81\jdbc\lib\classes12.zip

NomRépPartagé est le nom de partage donné au répertoire Oracle sur le serveur.

 

Le listener devra être démarré sur le serveur.

a) Chargement du pilote

Il faut d’abord charger le pilote pour permettre la connexion à la base, ce chargement se fait grâce à un appel au DriverManager (gestionnaire de pilotes) :

try 
{
               Class.forName("oracle.jdbc.driver.OracleDriver");
               // ou DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
}
catch(ClassNotFoundException e) 
{
               System.out.println("JDBC driver not found.");
}

 

b) Connexion proprement dite

Nous allons partir de l'exemple de la base des employés sous Oracle.

 

String cochaine = "jdbc:oracle:oci8:@nomserv";    // nomserv désigne le nom du                                                                                             // service créé sous Net8

Connection conn = DriverManager.getConnection(cochaine, "scott", "tiger");   // on effectue

                                                                                                                      // la connexion  

 

 

2.1.2       Pilotes 100% Java

 

Nous illustrons ce paragraphe avec Oracle.

Il faudra que Oracle Thin Driver soit installé sur le serveur et il faut que le classpath contienne le chemin de la bibliothèque des drivers Oracle donc le classpath sera :

.;\\NomServeur\NomRépPartagé\Ora81\jdbc\lib\classes12.zip

NomRépPartagé est le nom de partage donné au répertoire Oracle sur le serveur.

Le listener devra être démarré sur le serveur.

2.1.2.1  Chargement du pilote

Il faut d’abord charger le pilote pour permettre la connexion à la base, ce chargement se fait grâce à un appel au DriverManager (gestionnaire de pilotes) :

try 
{
               Class.forName("oracle.jdbc.driver.OracleDriver");
                // ou DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
}
catch(ClassNotFoundException e) 
{
               System.out.println("JDBC driver not found.");
}

 

2.1.2.2  Connexion proprement dite

Nous allons partir de l'exemple de la base des employés sous Oracle.

 

String cochaine = "jdbc:oracle:thin:@192.168.1.36:1521:btsig";    

Connection conn = DriverManager.getConnection(cochaine, "scott", "tiger");   // on effectue

                                                                                                                      // la connexion  

 

On peut mettre le nom de la machine (sur Internet, il s'agira d'un nom pleinement qualifié) à la place de l’adresse IP.

 

 

3         Le client-serveur de données

 

Quand la connexion est établie (si aucune exception n'a été interceptée) on peut écrire des requêtes pour construire, interroger ou  mettre à jour la base.

Plusieurs interfaces sont utilisées :

Statement : C'est une interface que l'application emploie pour transmettre des requêtes SQL à la base.

ResultSet : un ResultSet sera créé lors de l’exécution d’une requête d’interrogation, c’est  l’ensemble des tuples du résultat de la requête, cet ensemble pourra bien sûr être manipulé.

 

En cas d’échec de l’une des méthodes permettant de travailler sur les données, c’est l’exception SQLException qui est levée. Il est obligatoire d’intercepter cette exception ou bien de la renvoyer. C'est l'exception SQLException qui est levée en cas d'erreur pour chacune des opérations vues dans la suite de ce cours.

 

3.1      Interrogation de la base

 

3.1.1       Requête et curseur (ResultSet)

 

Il faut commencer par créer un Statement, il s’appuie sur la connexion créée précédemment :

 Statement requete = conn.createStatement();

 

 

executeQuery exécute la requête et place le résultat dans un ResultSet. Cet objet (nommé resultat dans l’exemple ci-dessous) permet d'accéder aux données du résultat de la requête :

  ResultSet resultat = requete.executeQuery ("select empno, ename, sal, hiredate from emp");

 

 

Après la requête, le curseur est positionné juste avant la première ligne du résultat mais le 1er enregistrement n’est pas accessible, il faut effectuer un next() comme par la suite pour avancer d’enregistrement en enregistrement.

 resultat.next();

 

La fin du ResultSet est détectée par le renvoi de la valeur false.

 

Il faut donc effectuer une lecture initiale, puis prévoir une boucle incluant l’utilisation des données et la lecture du suivant.

 

boolean bool=resultat.next();

if  (bool)

{

            do

            {

                        ...

                        ...

                        bool=resultat.next();               }

            }

            while (bool) ;

}

else

            …

 

Facilité d’écriture :

while (resultat.next()) et if (resultat.next()) assurent la lecture de l’enregistrement et la détection de la fin du ResultSet.

 

if  (resultat.next())

{

            do

            {

                        ...

                        ...

            }

            while (resultat.next()) ;

}

else

            …

 

Si on ne souhaite pas distinguer le cas ResultSet vide, on utilisera un TANTQUE et la structure du traitement sera la suivante :

while (resultat.next())

{

            ...

            ...        

}

 

 

3.1.2       Récupération des données

 

Pour récupérer les données des différents champs, il faut utiliser la méthode getObject (qui permet de récupérer les données sans se soucier de leurs types mais attention, cela ne fonctionne généralement qu’avec le pont JDBC/ODBC et à la condition qu’il ne s’agisse pas de données de type réel ou date ou heure) ou utiliser les méthodes adaptées aux types des données à récupérer mais alors il faut connaître le type des données récupérées.

De plus, si on utilise getObject, la donnée récupérée ne sera pas « typée » et il sera par exemple impossible d’effectuer des opérations arithmétiques sur cette donnée à moins de la convertir.

 

Attention les colonnes sont numérotées à partir de 1.

Principales méthodes  de récupération des champs suivant le type:

Type SQL

Type Java

Méthode de récupération

CHAR, VARCHAR, VARCHAR2

String

 getString(NumColonne)

NUMERIC (NUMBER sous ORACLE) sans partie décimale

int ou long ou java.math.BigDecimal

getInt(Numcolonne) ou getLong(NumColonne) ou getBigDecimal(NumColonne)

NUMERIC (NUMBER sous ORACLE) avec partie décimale

double ou float

getDouble(Numcolonne) ou

getFloat(NumColonne) ou

getBigDecimal(NumColonne)

SMALLINT

short

getShort(NumColonne)

INTEGER

int

getInt(NumColonne)

DATE

Java.sql.Date

getDate(NumColonne)

TIME ou partie heure de DATE

java.sql.Time

getTime(NumColonne)         

 

Remarques :

. Ces méthodes sont surchargées pour permettre la transmission du nom de la colonne à la place de son numéro.


 

Récupération et affichage des colonnes issues de la requête précédente :

System.out.println("Numéro employé : " + resultat.getObject(1));

System.out.println("Nom : "+" " + resultat.getObject(2));

System.out.println("Sal : "+" " + resultat.getFloat("sal"));

System.out.println("Date d'embauche : "+" " + resultat.getDate("hiredate" ));

 

 

3.1.3       Exemple global

 

Version avec renvoi des exceptions :

import java.sql.*;

class ExGlobal

{

public static void main (String args[]) throws ClassNotFoundException,

                                                                       SQLException

            {

                        String cochaine="jdbc:odbc:ora8";

                        Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");

                        Connection conn = DriverManager.getConnection(cochaine,"scott","tiger");

                        Statement requete =   conn.createStatement();                             

                        ResultSet resultat = requete.executeQuery("SELECT

                                                           empno,ename,sal,hiredate FROM EMP");

                                  

                        if  (resultat.next())

                        {

                                                                                                                                                                                 System.out.println("Numéro  Nom     Salaire Date d'embauche");

                                   do

                                   {

                                               // Si on n’avait pas utilisé le pont JDBC/ODBC pour la

                                               // connexion, on n’aurait pas pu utiliser getObject.              

                                               System.out.println(resultat.getObject(1)+"\t"+

                                               resultat.getObject(2) + "\t" + resultat.getFloat("sal")+ "\t" +

                                               resultat.getDate("hiredate"));

                                   }

                                   while (resultat.next()) ;

                        }

                        else

                                   System.out.println (“La table des employés est vide”);

                        resultat.close() ;  // Libération des ressources occupées par l’objet resultat

                        requete.close() ; // Libération des ressources occupées par l’objet requete

                        conn.close(); // Libération des ressources occupées par l’objet conn

            }

}

 

 

 

Version avec interception des exceptions :

import java.sql.*;

class ExGlobal

{

public static void main (String args[])

            {

                        try

                        {                     

                                   String cochaine="jdbc:odbc:ora8";

                                   Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");

                                   Connection conn =     

                                               DriverManager.getConnection(cochaine,"scott","tiger");

                                   Statement requete =   conn.createStatement();                            

                                   ResultSet resultat = requete.executeQuery

                                               ("SELECT empno,ename,sal,hiredate FROM EMP");

                                  

                                   if  (resultat.next())

                                   {

                                                                                                                                                                                            System.out.println ("Numéro  Nom     Salaire Date d'emb.");

                                               do

                                               {

                                                           // Si on n’avait pas utilisé le pont JDBC/ODBC pour la

                                                           // connexion, on n’aurait pas pu utiliser getObject.                                                              System.out.println(resultat.getObject(1)+"\t"+

                                                           resultat.getObject(2) + "\t" + resultat.getFloat("sal")+ "\t"

                                                           + resultat.getDate(4));

                                               }

                                               while (resultat.next()) ;

                                   }

                                   else

                                               System.out.println (“La table des employés est vide”);

                                   resultat.close() ;

                                   requete.close() ;

                                   conn.close();

                        }

                        catch(SQLException e)

                        {

                                   System.out.println("Erreur\n" + e.getMessage());  // e contient le nom 

                                   // de l’exception et le message d’erreur, e.getMessage() renvoie

                                   // uniquement le message d’erreur

                                   e.printStackTrace();  // Affichage de la pile des appels de méthodes

                        }                     

                        catch(ClassNotFoundException e) 
                        {
                                    System.out.println("Le pilote JDBC n'a pas été trouvé");

                                    e.printStackTrace();  // Affichage de la pile des appels de méthodes

                        }
            }

}         

 

3.1.4       Méta-données

Il est possible de récupérer des informations sur la base (nom du SGBD, nom des tables, des index…, nom de l’utilisateur courant…) grâce à l’interface DataBaseMetaData et des informations sur un ResultSet (nombre de colonnes, nom, type… des colonnes) grâce à l’interface ResultSetMetaData.

 

DatabaseMetaData dbmd = conn.getMetaData();   // retourne les méta-données de la base

System.out.println(dbmd.getDatabaseProductName());

System.out.println(dbmd.getUserName());

 

 

Cette interface fournit des informations sur un ResultSet, comme :

Pour le type, le problème est que pour NUMERIC (ou NUMBER sous Oracle), la méthode renvoie 3 qu’il y ait une partie décimale ou non.

Exemple : on reprend l’exemple précédent en utilisant l’interface ResultSetMetaData pour l’affichage de l’en-tête.
 

if  (resultat.next())

{

            ResultSetMetaData rsmd;

            rsmd = resultat.getMetaData();

for (int i=1;i<=rsmd.getColumnCount();i++)

                        System.out.print(rsmd.getColumnName(i) + "\t");

            System.out.println("");

            do

                        System.out.println(resultat.getObject(1)+"\t"+ resultat.getObject(2) +

                                   "\t" + resultat.getFloat("sal")+ "\t" + resultat.getDate("hiredate"));

            while (resultat.next()) ;

}

else

System.out.println (“La table des employés est vide”);

 


 

3.1.5       Requête paramétrée

 

Il est tout à fait possible de concaténer des parties fixes et des parties variables pour constituer le texte de la requête.

 

Exemple avec une date :

String demb = "02-04-1981";

ResultSet resultat = requete.executeQuery

("SELECT empno,ename, sal FROM EMP where hiredate >= '" + demb + "'");

                                  

Exemple avec un entier :

int dept = 10;                          

ResultSet resultat = requete.executeQuery

            ("SELECT empno,ename,sal,hiredate FROM EMP where deptno = " + dept);

 

 

3.1.6       Requête précompilée

 

 Il faut pour cela utiliser l’interface PreparedStatement  à laquelle on associe une requête et un type de curseur (curseur en lecture seule et sans positionnement par défaut).

L’interface PreparedStatement dérive de Statement.

La requête pourra être paramétrée.

 

Deux ou trois étapes pour obtenir le ResultSet :

 

·         Préparation de la requête :

PreparedStatement pstmt = conn.prepareStatement

                                                           ("select empno,ename from emp where ename = ?");

 

? désigne l'emplacement d'un paramètre.

 

Chaque paramètre doit être renseigné en appliquant une méthode

setType (NuméroParamètre, Valeur) sur l’objet PreparedStatement.

 

pstmt.setString(1,"MILLER"); // renseignement du 1er et seul paramètre avec la

                                                 // valeur MILLER

 

Les principales méthodes setType sont :

setString(NumColonne, Valeur)

setInt(NumColonne, Valeur)

setLong(NumColonne, Valeur)

setFloat(NumColonne, Valeur)

setDouble(NumColonne, Valeur)

setDate(NumColonne, Valeur)

 

ResultSet rs = pstmt.executeQuery();

 

Remarque : si on souhaite exécuter plusieurs fois la requête, il suffira d’appliquer uniquement la méthode executeQuery à l’objet PreparedStatement, il n’y pas à nouveau compilation de la requête. Ceci est vrai même pour une requête paramétrée car en fait la requête est précompilée sans les valeurs des paramètres. Bien sûr, si on souhaite modifier la valeur des paramètres, il faudra à nouveau utiliser les méthodes setType.

 

import java.sql.*;

import java.io.*;

class Reqpar

{

            public static void main (String args[]) throws ClassNotFoundException,

                        SQLException, IOException

            {

                        Class.forName("oracle.jdbc.driver.OracleDriver");

                        Connection conn = DriverManager.getConnection

                                   ("jdbc:oracle:thin:@192.168.10.36:1521:btsig", "scott", "tiger");

 

                        PreparedStatement pstmt = conn.prepareStatement

                                   ("select empno,ename,sal,job from emp where deptno = ?");

                        do

                        {

                                   pstmt.setInt (1,Entrée.entier("\nSaisir le n° de département"));

                                   // Si le numéro saisi n’existe pas, l’en-tête seule sera affichée

                                   System.out.println ("Numéro  Nom     Salaire Date d'embauche");

                                   ResultSet rs = pstmt.executeQuery();

                                   while (rs.next())

                                               System.out.println (rs.getInt(1)+" " + rs.getString(2)+

                                               " " + rs.getFloat(3) + " " + rs.getString(4));

                                   rs.close();

                        }

                        while (Entrée.car("\nAutre département(O/N)")=='O');

                        pstmt.close() ;

                        conn.close();

            }                     

}

 

 

3.1.7       Traitement avancé des ResultSet

 

Il est possible de déclarer un ResultSet sur lequel on peut effectuer les déplacements et positionnements que l’on veut, on parle alors de ResultSet navigable.

Jusqu’à maintenant, nous avons créé un Statement sans préciser de paramètres, le ResultSet est alors uniquement en consultation et peut être uniquement parcouru du début à la fin (pas de retour ni de positionnement possible), il est dit non navigable.

 

Il est possible de préciser 2 paramètres :

1er paramètre :navigabilité et visibilité des mises à jour des autres :

                        ResultSet.TYPE_FORWARD_ONLY : non navigable et on ne voit pas les mises à jour des autres

                        ResultSet.TYPE_SCROLL_INSENSITIVE : navigable et on ne voit pas les mises à jour des autres

                        ResultSet.TYPE_SCROLL_SENSITIVE : navigable et on voit les mises à jour des autres.

 

2ème paramètre : lecture seule ou mise à jour :

ResultSet.CONCUR_READ_ONLY               

                        ResultSet.CONCUR_UPDATABLE              

 

Remarque : les 2 paramètres qui viennent d’être étudiés pour le createStatement peuvent être mis en 2ème et 3ème paramètres du preparedStatement.

 

 

Méthodes pour les déplacements en plus de la méthode next():
public void afterLast()       // Positionne le pointeur après le dernier ergt
public void beforeFirst()   // Positionne le pointeur avant le premier ergt
public boolean previous()  // Positionne le pointeur sur l’ergt précédent et retourne true si OK 
                                              // et false sinon
public boolean first()             // Déplace le pointeur sur le 1er ergt et retourne true si OK et false
                                              // sinon 
public boolean last()              // Déplace le pointeur sur le dernier ergt et retourne true si OK et 
                                              // false sinon 
 
public boolean relative(int nombre_enregistrements) // Déplace le pointeur de 
                               nombre_enregistrements plus loin ou recule de nombre_enregistrements (si
                               valeur négative). Il faut déjà être positionné sur un ergt
public boolean absolute(int position)                  // Déplace le pointeur sur l’enregistrement 
                                              // indiqué (la position est numérotée à partir de 1). Ne nécessite 
                                              // pas de positionnement préalable.                                                     
 

Exemple :

import java.sql.*;

class navigation

{

            public static void main (String args[]) throws ClassNotFoundException,

            SQLException

            {

                        Class.forName("oracle.jdbc.driver.OracleDriver");

                       

                        Connection conn = DriverManager.getConnection

                                   ("jdbc:oracle:thin:@192.168.10.36:1521:btsig", "scott", "tiger");

                        Statement requete = conn.createStatement

                                   (ResultSet.TYPE_SCROLL_INSENSITIVE,

                                   ResultSet.CONCUR_READ_ONLY);

                        ResultSet resultat = requete.executeQuery

                                   ("SELECT empno,ename, sal,hiredate FROM EMP");

                        resultat.afterLast();

                        if  (resultat.previous())

                        {

                                   ResultSetMetaData rsmd;

                                   rsmd = resultat.getMetaData();

                                              

                                   for (int i=1;i<=rsmd.getColumnCount();i++)

                                               System.out.print(rsmd.getColumnLabel(i) + "\t");

                                   System.out.println("");

                                   do

                                               System.out.println(resultat.getInt(1)+"\t"+ 

                                               resultat.getString(2) + "\t" + resultat.getFloat("sal")+ "\t"

                                                + resultat.getDate(4));

                                   while (resultat.previous()) ;

                                  

                                   // Après avoir traité le ResultSet d'arrière en avant, on peut très bien

                                   // aller au début et le traiter du début à la fin

                                   resultat.beforeFirst();

                                   resultat.next();

                                   // …

                                   resultat.close();

                                   requete.close();

                                   conn.close();

                        }

            }

}

 

 

3.2      Mise à jour (au sens large) de la base de données

 

            La mise à jour d'une base de données peut être effectuée :

 

ou


 

3.2.1       Ajout

 

·         En utilisant une requête SQL

 

Exemple :

Statement st = conn.createStatement();
st.executeUpdate("INSERT INTO dept values (50, ‘INFORMATIQUE’, ‘BATIMENT C’") ;

// Si on effectue l’INSERT avec SELECT au lieu de VALUES alors il est possible d’insérer

// plusieurs lignes à la fois et il peut être intéressant de récupérer le nombre de lignes insérées

int nbIns = st.executeUpdate("INSERT INTO dept SELECT …") ;

System.out.println (nbIns + " ligne(s) insérée(s)");

 

 

·         En utilisant un curseur

 

Exemple :

ResultSet resultat = requete.executeQuery

                                          ("SELECT empno,ename, sal,hiredate, deptno FROM EMP");

resultat.moveToInsertRow();  // On se met en insertion

resultat.updateInt(1,7000);

resultat.updateString(2,"TITI");

resultat.updateFloat("sal",100);

resultat.updateInt(5,30);

resultat.insertRow();  // On ajoute la ligne dans la table

 

Les principales méthodes updateType sont :

updateString(NumColonne, Valeur)

updateInt(NumColonne, Valeur)

updateLong(NumColonne, Valeur)

updateFloat(NumColonne, Valeur)

updateDouble(NumColonne, Valeur)

updateDate(NumColonne, Valeur)

 

Remarque :

Ainsi qu'on peut le voir dans l'exemple, ces méthodes sont surchargées pour permettre la transmission du nom de la colonne à la place de son numéro.

 

           

3.2.2       Modification

 

·         En utilisant une requête SQL

 

Exemple :

st.executeUpdate("UPDATE emp set sal = sal * 1.05”); // st est un Statement

 


 

·         En utilisant un curseur

 

On est positionné sur un enregistrement, on effectue la modification des champs puis on effectue la modification de l’enregistrement.

 

Exemple :

ResultSet resultat = requete.executeQuery

                              ("SELECT empno,ename, sal,hiredate FROM EMP");

if  (resultat.next())

{

            do

            {

                        System.out.println (resultat.getInt(1)+"\t"+ resultat.getString(2) +

                                   "\t" + resultat.getFloat("sal")+ "\t" + resultat.getDate(4));

 

                        float nouvsal = Entrée.flottant

                              ("Employé " + resultat.getInt(1) + ", saisir le nouveau salaire : ");

                        resultat.updateFloat(3, nouvsal);   

                        resultat.updateRow();

            }

            while (resultat.next()) ;

}

 

 

           

3.2.3       Suppression

 

·         Par une requête SQL

 

st.executeUpdate("DELETE FROM emp where ename = 'MILLER'");

 

·         En utilisant un curseur

L'exemple qui suit montre la suppression de la ligne 5 d'un ResultSet.

 

 

ResultSet resultat = requete.executeQuery

                             ("SELECT empno,ename, sal,hiredate FROM EMP");

resultat.absolute(5);  
resultat.deleteRow(); // Suppression de la ligne en cours

 

 

3.2.4       Requête de mise à jour (au sens large) paramétrée

 

C’est le même principe que pour un SELECT c’est-à-dire que la requête INSERT, UPDATE ou DELETE peut contenir des parties fixes et des parties variables.

 

 

3.2.5       Requête de mise à jour (au sens large) précompilée

 

Cela s’effectue avec un objet PreparedStatement comme pour une requête de consultation (excepté que l’exécution sera effectuée avec executeUpdate). La requête pourra être paramétrée ou non.

 

Exemple :

 

import java.sql.*;

import java.util.*;

class maj2

{

            public static void main (String args[]) throws ClassNotFoundException,

SQLException

            {

                        String url="jdbc:odbc:micor";

            Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");

                        Connection conn = DriverManager.getConnection(url,"scott","tiger");

                        java.sql.Date dsys = new java.sql.Date(System.currentTimeMillis());

                        PreparedStatement  pstmt = conn.prepareStatement

                                   ("insert into emp values (3,'TOTO','nn',7000,?,10,10,10)");

                        pstmt.setDate(1,dsys); 

                        pstmt.executeUpdate();                    

}

}

 


DEVELOPPEMENT CLIENT-SERVEUR EN JAVA (suite)

    

 

Bref rappel de la 1ère partie à partir d’un exemple :

import java.sql.*;

class ExGlobal

{

public static void main (String args[]) throws ClassNotFoundException,

                                                                       SQLException

            {

                        String cochaine="jdbc:odbc:ora8";

                        Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");

                        Connection conn = DriverManager.getConnection(cochaine,"scott","tiger");

                        Statement st =   conn.createStatement();                            

                        ResultSet resultat = st.executeQuery("SELECT empno, ename, sal,

                                                                                  hiredate FROM EMP");

                                  

                        if  (resultat.next())

                        {

                                                                                                                                                                                 System.out.println("Numéro  Nom     Salaire Date d'embauche");

                                   do

                                   {

                                               // Si on n’avait pas utilisé le pont JDBC/ODBC pour la

                                               // connexion, on n’aurait pas pu utiliser getObject.              

                                               System.out.println(resultat.getObject(1)+"\t"+

                                               resultat.getObject(2) + "\t" + resultat.getFloat("sal")+ "\t" +

                                               resultat.getDate("hiredate"));

                                   }

                                   while (resultat.next()) ;

                        }

                        else

                                   System.out.println (“La table des employés est vide”);

                        resultat.close() ;  // Libération des ressources occupées par l’objet resultat

                        st.close() ; // Libération des ressources occupées par l’objet st

                        conn.close(); // Libération des ressources occupées par l’objet conn

            }

}

 

 

 

 

 

 

 

 

 

 

 

 

4         Les requêtes de définition ou de contrôle des données

 

Avec la méthode executeUpdate, on peut également effectuer toute requête du LDD

(Exemple : create table) ou du LCD (Exemple : grant).

 

Exemple :

Statement st = conn.createStatement();                   

st.executeUpdate ("grant all on emp to public");

 

 

 

4.1      La validation/annulation des mises à jour (au sens large)effectuées

 

Avec JDBC, par défaut, un commit est effectué après chaque mise à jour. Si on ne souhaite pas le mode par défaut, il faut passer en mode manuel :

conn.setAutocommit(false);

et ensuite, il faudra se charger du commit ou rollback :

conn.commit();

ou

conn.rollback();

 

On peut repasser à tout moment en mode autocommit :

conn.setAutocommit(true) ;

 

Il faut préciser que par défaut (mode autocommit) : en cas de plantage, les mises à jour effectuées ne sont pas « défaites » par jdbc ; ceci est dû au fait qu’un commit est effectué après chaque instruction.

 

 

5         Le client-serveur de traitements

 

5.1      L'exécution de procédures stockées

 
On utilise pour cela l'interface CallableStatement qui dérive de PreparedStatement.
 
Bien sûr, une procédure stockée peut être paramétrée.
Il faut d'abord préparer l'appel puis valoriser les paramètres (si procédure paramétrée) puis exécuter l'appel.
 

5.1.1       Syntaxe

 
·         Préparation :
Sans paramètres :

CallableStatement cStmt = conn.prepareCall("{call nom_Procédure}");

 

Avec paramètres :

CallableStatement cStmt = conn.prepareCall("{call nom_Procedure (?, ?, ...)}");
Il faut ensuite valoriser les paramètres au moyen des méthodes setType (setInt …). Si les paramètres sont uniquement en entrée, on peut indiquer leurs valeurs directement dans l'appel à la méthode prepareCall.
 
·         Exécution :

cStmt.executeUpdate();

 
 

5.1.2       Exemples

 

 

CallableStatement cStmt =conn.prepareCall("{call majsal}");

cStmt.executeUpdate();

 

 

Code PL/SQL de la procédure :

 

CREATE PROCEDURE majsal

BEGIN

            DECLARE   NumSal int;

            DECLARE   montant float;

            SET NumSal = 7934;

            SET montant = 100;

            Update emp set sal = sal + montant

            Where empno = NumSal ;

END majsal ;

 

 

 

CallableStatement cStmt =conn.prepareCall("{call majsal2(?,?)}");

cStmt.setInt(1,7934);

cStmt.setFloat(2,5000.5F);

cStmt.executeUpdate();

 

On pouvait également transmettre directement les valeurs des paramètres à la méthode prepareCall :

 

CallableStatement cStmt =conn.prepareCall("{call majsal2(7934,5000.5F)}");

cStmt.executeUpdate();

 

Code PL/SQL de la procédure :

 

create procedure majsal2(x_empno int, x_sal float)

BEGIN

    UPDATE EMP SET sal = sal + x_sal WHERE empno = x_empno;

END majsal2;

 

 

CallableStatement cStmt =

conn.prepareCall("{call majsal3(?,?)}");

cStmt.setInt(1,7934);

cStmt.setFloat(2,4000);

cStmt.registerOutParameter(2, Types.FLOAT);       // Obligatoire pour les paramètres en

                                                                                  // e/s ou en sortie

                                                                      

cStmt.executeUpdate();

 

System.out.println("Nouveau salaire : " + cStmt.getFloat(2));

 

Code PL/SQL de la procédure :

 

create or replace procedure majsal3(x_empno in int, x_sal in out float)

BEGIN

    UPDATE EMP SET sal = sal + x_sal WHERE empno = x_empno;

    select sal into x_sal from emp where empno = x_empno;

END majsal3;

 

Remarques : le principe pour les paramètres en sortie est le même que pour les paramètres en e/s excepté qu’il n’y a pas d’instruction setType pour alimenter le paramètre.

Des problèmes se posent pour les paramètres en e/s et en sortie si on utilise le pont jdbc/odbc.

 

 

5.2      L'exécution de fonctions stockées

 

C'est le même principe que pour les procédures stockées excepté qu'il y a un paramètre de retour.

 

5.2.1       Syntaxe

 

·         Préparation :
Sans paramètres :

CallableStatement cStmt = conn.prepareCall("{? = call nom_Fonction}");

 

Avec paramètres :

CallableStatement cStmt = conn.prepareCall("{? = call nom_Fonction (?, ?, ...)}");
Il faut ensuite valoriser les paramètres au moyen des méthodes setType (setInt…). Si les paramètres sont uniquement en entrée, on peut indiquer leurs valeurs directement dans l'appel à la méthode prepareCall.
 
·         Exécution :

cStmt.executeUpdate();

Ensuite, il faut récupérer le paramètre de retour au moyen d'une méthode getType.

 

5.2.2       Exemple

 

CallableStatement cStmt = conn.prepareCall("{? = call MoyenneAnc('SALESMAN')}");

 

cStmt.registerOutParameter(1,Types.INTEGER);   // Obligatoire pour le paramètre de retour

cStmt.executeUpdate();

 

System.out.println(cStmt.getInt(1));  // Le paramètre de retour est toujours le 1er paramètre

 

Code PL/SQL de la fonction :

 

CREATE FUNCTION MoyenneAnc  (jobsai varchar(40))

RETURNS int

BEGIN

            DECLARE ancmoyen int;

            select avg(YEAR(sysdate')  - YEAR(hiredate))

            into ancmoyen

            from emp

            where job = jobsai;

            RETURN  ancmoyen;

END MoyenneAnc;