DEVELOPPEMENT CLIENT-SERVEUR EN JAVA
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.
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 :
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.
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) |
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.
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.
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.");
}
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
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.
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.");
}
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.
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.
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())
{
...
...
}
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" )); |
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
}
}
}
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.
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”);
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);
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();
}
}
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();
}
}
}
La mise à jour d'une base de données peut être effectuée :
ou
· En utilisant une requête SQL
Exemple :
Statement st =
conn.createStatement(); // 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.
· 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()) ; } |
|
· 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);
|
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.
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
}
}
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");
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.
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.
· 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();
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.
C'est le même principe que pour les procédures stockées excepté qu'il y a un paramètre de retour.
· 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.
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;