Ch. 3 — JDBC : du programme à la base
Connecter un programme Java à une base : le pilote et son classpath, l'URL de connexion propre à chaque SGBD, Statement et ResultSet, la fermeture propre en try-with-resources — et la lecture des messages d'erreur.
Après les rappels SQL et le chapitre client-serveur/ODBC, on passe au code : comment un programme Java dialogue avec une base de données. La réponse s'appelle JDBC — Java DataBase Connectivity — et c'est l'équivalent Java d'ODBC : une API standard, fournie avec Java SE, qui permet au même programme de parler à n'importe quel SGBD, à condition de disposer de son pilote.
1. JDBC, c'est quoi ?
- une API Java pour la connexion aux bases de données, fournie avec Java SE (paquetage
java.sql) ; - indépendante du système (contrairement à ODBC) et indépendante de la base de données (comme ODBC) ;
- le paquetage
java.sqlcontient :
| Catégorie | À retenir |
|---|---|
| Interfaces | Connection, Statement, PreparedStatement, ResultSet, Driver… |
| Classes | DriverManager, Date, Time, Timestamp, Types… |
| Exceptions | SQLException (et BatchUpdateException, SQLWarning) |
Ces noms sont le vocabulaire de tout le chapitre — on les croise dans chaque programme.
2. Enregistrer le pilote
Comme pour ODBC, chaque base de données utilise un pilote (driver) qui lui est propre : c'est lui qui convertit les appels JDBC dans le langage natif du SGBD. La première étape d'un programme, c'est de l'enregistrer :
Class.forName("org.gjt.mm.mysql.Driver"); // MySQL
Class.forName("oracle.jdbc.driver.OracleDriver"); // Oracle
Class.forName("org.postgresql.Driver"); // PostgreSQL
Class.forName("org.h2.Driver"); // H2Où vit le pilote ?
C'est le serveur qui fournit le pilote, sous forme d'une archive jar — mais le
pilote est toujours installé sur le poste client ! C'est votre programme qui doit pouvoir
le charger au moment de l'exécution (voir section 8).
3. L'URL de connexion
Une fois le pilote enregistré, on établit la connexion avec la base :
Connection con = DriverManager.getConnection(URL, utilisateur, mdp);Chaque pilote possède sa propre syntaxe d'adresse :
url = "jdbc:mysql://localhost:3306/mabase";
url = "jdbc:oracle:thin:@127.0.0.1:8888:mabase";
url = "jdbc:postgresql://localhost:5432/mabase";
url = "jdbc:h2:tcp://localhost:9092/~/mabase";L'URL de la base dépend du pilote — on retrouve la machine, le port (5432 pour PostgreSQL) et le nom de la base. C'est le même découpage qu'en ODBC, avec une autre grammaire.
4. Un test de connexion
Le programme minimal du cours : enregistrer le pilote, se connecter, fermer, annoncer la couleur.
import java.sql.*;
public class TestConnect {
public static void main(String args[]) throws Exception {
// enregistrement du driver
Class.forName("org.postgresql.Driver");
// connexion à la base
String url = "jdbc:postgresql://machine/base";
String nom = "xxx";
String mdp = "yyy";
Connection con = DriverManager.getConnection(url, nom, mdp);
// fermeture de la connexion
con.close();
System.out.println("All is ok !");
}
}À la compilation et à l'exécution, une différence capitale :
javac TestConnect.java # le driver n'est pas nécessaire
java -cp ~/jars/mondriver.jar:. TestConnect # mais il est impératif ici !Grâce au class loader, le pilote n'est pas nécessaire à la compilation. Il est
impératif à l'exécution, et il se déclare sur le classpath. Rappels sur -cp :
- dès que
-cpest utilisé, il faut spécifier tous les chemins de recherche des classes et.jar; - quand les dépendances se multiplient, préférez la variable système
CLASSPATH; - attention :
CLASSPATH≠PATH(l'un cherche des classes, l'autre des exécutables).
5. Interroger la base
La technique générale tient en trois temps :
- créer une zone de gestion de requête :
Statement stmt = con.createStatement(); - exécuter la requête :
ResultSet rs = stmt.executeQuery(query);pour unSELECT;int nb = stmt.executeUpdate(query);pourINSERT,UPDATE,DELETE(renvoie le nombre de lignes touchées) ;
- fermer les espaces :
close()des objetsResultSet,StatementetConnection.
Toujours fermer la connexion !
Bonne nouvelle : close() sur la connexion entraîne automatiquement la fermeture des
autres objets. Mauvaise nouvelle : il faut penser à l'appeler, y compris quand le
programme se passe mal (section 7).
Les trois exemples du cours, en entier :
import java.sql.*;
public class Create {
public static void main(String args[]) throws Exception {
Class.forName("org.postgresql.Driver");
String url = "jdbc:postgresql://localhost/mabase";
Connection con = DriverManager.getConnection(url, "mathieu", "xxx");
Statement stmt = con.createStatement();
stmt.executeUpdate("create table CLIENT " +
"(CNO int PRIMARY KEY, NOM varchar(10), PRENOM varchar(10), AGE int)");
con.close();
System.out.println("All is ok !");
}
}// …
Statement stmt = con.createStatement();
stmt.executeUpdate("insert into CLIENT " +
"values(1,'Durand','paul',10)");
con.close();// …
Statement stmt = con.createStatement();
String query = "select NOM,PRENOM,AGE from CLIENT";
ResultSet rs = stmt.executeQuery(query);
System.out.println("Liste des clients:");
while (rs.next()) {
String n = rs.getString("nom"); // col 1
String p = rs.getString("prenom"); // col 2
int a = rs.getInt("age"); // col 3
System.out.println(n + " " + p + " " + a);
}
con.close();Lire un ResultSet
executeQuery renvoie un objet de type ResultSet avec deux méthodes à connaître :
next()— permet de parcourir leResultSet: renvoietruetant qu'il y a des tuples. Impossible de revenir en arrière !getXXX(arg)— permet de lire une donnée dans une variable de typeXXX(getInt,getFloat,getDate,getLong,getString…). Le type est celui de la variable qui récupère le résultat. L'argument peut être le nom de la colonne ou son numéro :String pds = rs.getString("poids");
6. Passer des paramètres… par concaténation
Très souvent, la requête a besoin de valeurs rangées dans des variables Java. Le lien entre Java et SQL se fait par concaténation de chaînes :
int age = 10;
String query = "select * from clients where age > " + age;
String nom = "Durand";
String query2 = "select * from clients where nom like '" + nom + "'";Pensez à systématiquement afficher la requête fabriquée
Avant de l'exécuter, affichez la requête construite : c'est le moyen le plus rapide de voir
une apostrophe mal placée ou une variable vide. Et gardez en tête que cette concaténation a
des limites — le chapitre 4 présente PreparedStatement, la façon propre de passer des
paramètres.
7. Fermer proprement : try/finally, puis try-with-resources
La fermeture des connexions est capitale — que le programme se passe bien ou mal. Première version, à la main :
Connection con = null;
try {
Class.forName("org.postgresql.Driver");
con = DriverManager.getConnection(url, nom, mdp);
// … les requêtes …
} catch (Exception e) {
// affichage ou traitement de l'erreur : impératif
} finally {
try { con.close(); } catch (Exception e2) { }
}Depuis Java 7, on peut faire beaucoup mieux : les objets Connection implémentent l'interface
Closeable, donc try-with-resources :
try (Connection con = DriverManager.getConnection(url, nom, mdp)) {
// … les requêtes …
} catch (Exception e) {
// affichage ou traitement de l'erreur : impératif
}Dans le cas d'un Closeable, la méthode close est appelée automatiquement dans tous les
cas — succès, exception, retour anticipé. C'est la version à retenir.
8. Pourquoi le class loader et pas un import ?
On peut enregistrer le pilote par réflexion (Class.forName("org.postgresql.Driver")) ou
l'importer classiquement (import org.postgresql.*) — mais le class loader a un avantage
décisif :
- il permet de changer d'implémentation sans recompiler ;
- le programme devient indépendant du SGBD (il ne fait référence qu'à
java.sql) ; - conséquence :
javacn'a pas besoin du driver, c'estjavaqui en a besoin.
9. Gérer les erreurs
Les erreurs classiques du premier contact :
| Message | Cause probable |
|---|---|
ClassNotFoundException | problème de classpath (le .jar n'est pas trouvé) |
java.net.ConnectException: Connection refused | la tentative de connexion a échoué (serveur éteint ? mauvais port ?) |
role xxx does not exist | l'utilisateur est inconnu du SGBD |
Comme toutes les entrées-sorties Java, les ordres JDBC déclenchent des exceptions qu'il faut impérativement traiter :
- la classe
Exceptionest affinée enSQLException; getMessage()permet d'obtenir le message en clair ;printStackTrace()affiche la pile d'exécution.
Impératif : apprendre à lire les messages d'erreur !
10. Les 7 points capitaux
- le classpath contient tous les chemins de recherche des classes et
.jar; - deux possibilités pour l'exprimer : l'option
-cp, ou la variableCLASSPATH($CLASSPATHsous Unix,%CLASSPATH%sous Windows) ; - idéalement, on met la variable
CLASSPATHdans le.bashrcpour tous les shells ; - toujours afficher la requête avant son exécution ;
- toujours afficher
e.getMessage()dans lecatch; - sous Unix les séparateurs de répertoires sont
/, sous Windows\; - les variables système commencent par
$sous Unix et sont entourées de%sous Windows.
A retenir
- JDBC est fourni avec Java SE (
java.sql) : le même programme parle à n'importe quel SGBD, comme ODBC mais côté Java ; - le pilote (archive
.jar) est installé sur le poste client ; il n'est pas nécessaire à la compilation, mais impératif à l'exécution (classpath !) ; - le cycle d'une requête :
DriverManager.getConnection→createStatement→executeQuery/executeUpdate→ResultSet(next/getXXX) →close; - on ferme toujours la connexion — en try-with-resources ;
- devant une erreur : afficher la requête et
e.getMessage(), et apprendre à les lire.
cours3-jdbc.pdf
transp_cours303 — Java DataBase Connectivity · 22 diapositives
Chargement du document…
Dernière mise à jour le
Ch. 2 — Architecture client-serveur et ODBC
Pourquoi une base n'est presque jamais seule sur sa machine : le modèle client-serveur, les ports, les pilotes ODBC, et ce que tout cela change quand plusieurs personnes accèdent aux mêmes données.
Ch. 4 — JDBC évolué
Gagner en efficacité avec JDBC : clés générées automatiquement, curseurs de mise à jour, requêtes batch, PreparedStatement et sa boucle de paramètres, image en base — et la méta-base pour décrire la base depuis le code.