JDBC (Java DataBase Connectivity)

Page 1 sur 17Lecteur de document UniversityLib

JDBC (Java DataBase Connectivity)

Software Development / Java Programming · notes

Voir tous les documents en programmation

JDBC

Java DataBase Connectivity

JDBC

Mme CHALOUAH Anissa

Page | 1

1.JDBC (Java DataBase Connectivity)

1.1.

Problème

Accéder à une base de données depuis une application Java.

Application

JAVA

BD

1.2. Définition

JDBC est une API (bibliothèque d’interfaces et de classes) java standard qui permet un accès homogène à des bases de données depuis un programme Java au travers du langage SQL.

C’est de plus une tentative de standardiser l’accès aux bases de données car l’API est indépendante du SGBD choisi, pourvu que le pilote JDBC existe pour ce SGBD, et qu’il implémente les classes et interfaces de l’API JDBC.

1.3.

Avantages et inconvénients du JDBC

Avantages

• Comme JDBC repose sur SQL, cette bibliothèque bénéficie de toutes ses fonctionnalités éprouvées : création et mise à jour de tables, sélections avec jointure, transactions, appels à des procédures stockées.

• La durée d’apprentissage de JDBC est réduite pour les personnes utilisant déjà un SGBDR avec SQL.

• Pour permettre d’exploiter leur produit en Java, les éditeurs de SGBDR du marché (Oracle, Sybase...) n’ont qu’à développer un driver JDBC, ensemble de classes qui implémentent les interfaces du paquetage java.sql.

• Une application peut se connecter à plusieurs SGBDR en même temps.

• Si votre programme utilise la version standard de SQL, il suffit de changer le driver approprié en cas de changement de SGBDR.

Mme CHALOUAH Anissa

Page | 2

Inconvénients

• Les instructions SQL étant construites sous forme de chaînes de caractères, leur exactitude ne peut être vérifiée ni par le compilateur Java ni par JDBC, mais uniquement par le SGBDR au moment de leur traitement.

• JDBC n’oblige pas un éditeur de SGBDR à implémenter tout SQL dans son driver, ce qui peut gêner la portabilité d’une application Java en cas de changement de SGBDR.

• Basée sur SQL, JDBC est limité à l’utilisation de bases de données relationnelles : aucune utilisation de base de données objet (SGBDO) n’est prévue, fait étonnant pour un langage objet comme Java.

• JDBC peut difficilement évoluer indépendamment de SQL.

1.4.

Architecture de la technologie JDBC

1.5.

Pilotes JDBC

L'API JDBC se trouve dans java.sql et les pilotes doivent implémenter l'interface java.sql.Driver.

Il existe quatre types de pilotes jdbc :

• Type 1 : Pont jdbc-odbc (livré en standard, idéal comme premier driver sous Windows)

• Type 2 : API native + un peu de java.

• Type 3 : Comme type 2 mais avec un protocole réseau tout en java.

• Type 4 : Protocole natif 100% java.

Les éditeurs de SGBDR proposent leurs propres pilotes JDBC.

Mme CHALOUAH Anissa

Page | 3

Figure : Types de pilotes JDBC

2.JDBC et les architectures clients-

serveurs multi-tiers

2.1.

Architecture client-serveur 2/tiers

Dans une architecture client-serveur 2/tiers, un programme client accède directement à une base de données sur une machine distante (le serveur) pour échanger des informations via des commandes SQL JDBC automatiquement traduites dans le langage de requête propre au SGBD.

Le principal avantage de ce type d’architecture est qu’en cas de changement de SGBD, il n’y a qu’à mettre à jour ou changer le driver JDBC du coté client. Cependant, pour une grande diffusion du client, cette architecture devient problématique, car une telle modification nécessite la mise à jour de chaque client.

Protocole propriétaire

C B D J

Application

JAVA

Client

BD

Serveur de BD

Mme CHALOUAH Anissa

Page | 4

2.2.

Architecture 3/tiers

Dans une architecture 3/tiers, un programme client n’accède pas directement à la base de données, mais à un serveur d’application qui fait lui-même les accès à celle-ci. Il y a plusieurs avantages à cette architecture. Tout d’abord, il est possible de gérer plus efficacement les connexions au niveau du serveur d’application et d’optimiser les traitements. De plus, contrairement à l’architecture 2/tiers, un changement de SGBD ne nécessite pas une mise à jour des drivers sur tous les clients, mais seulement sur le serveur d’application.

Application Java

Navigateur Applet

Serveur d’application

JDBC

Protocole propriétaire

BD

Serveur de BD

3.Fonctionnement du JDBC

3.1.

Classes et interfaces de JDBC

a- Interfaces principales

- Driver : renvoie une instance de Connection

-

-

-

-

Connection : connexion à une base

Statement : ordre SQL

PreparedStatement : ordre SQL paramétré

CallableStatement : procédure stockée.

- ResultSet : lignes récupérées par un ordre SELECT

- ResultSetMetaData : description des lignes récupérées par un SELECT

-

DatabaseMetaData : informations sur la base de données

b- Classes principales

- DriverManager : gère les drivers, lance les connexions aux bases

-

-

Publicité

-

-

Date : date SQL

Time : heures, minutes, secondes SQL

TimeStamp : date et heure avec une précision à la microseconde

Types : constantes pour désigner les types SQL (pour les conversions avec les types

Java)

Mme CHALOUAH Anissa

Page | 5

3.2. Mise en œuvre de JDBC

Figure : Mise en Œuvre JDBC

Avant toute chose, n’oubliez pas d’importer le package java.sql

import java.sql.*;

1) Chargement du driver JDBC : Disposer du driver spécifique ou utiliser ODBC sinon, car

le pilote ODBC est dans l'API java.

2) Etablir la connexion au SGBD : Connaître l'URL de la base et y s'assurer d'avoir un

compte d'accès username/password.

3) Créer une requête (ou instruction SQL)

4) Exécuter la requête : Mise à feu de la transaction et retour des résultats

5) Traiter les données retournées

6) Fermer la connexion : Prendre soin de fermer les curseurs de dialogue liés à la

connexion

a. Etape 1 : Chargement du Driver

 Pour se connecter à une base de données il est essentiel de charger dans un premier temps le pilote de la base de données à laquelle on désire se connecter grâce à un appel au DriverManager (gestionnaire de pilotes) :

Class.forName("driver_name ");

Exemples

Chargement du pilote JDBC-ODBC :

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

Mme CHALOUAH Anissa

Page | 6

Chargement du pilote pour une base Oracle :

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

Chargement du pilote pour une base PostgreSQL :

Class.forName("org.postgresql.Driver");

 Quand une classe Driver est chargée, elle doit créer une instance d’elle même et

s’enregistrer auprès du DriverManager

 Certains compilateurs refusent cette notation et demande plutôt :

Class.forName("driver_name").newInstance();

 La méthode static forName() de la classe Class peut lever l'exception

java.lang.ClassNotFoundException.

Exemple

try {

Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { System.err.println("Driver loading error : " + e); }

b. Etape 2 : Connexion à la base

 Etablir une connexion en utilisant la classe java.sql.DriverManager. Son rôle est de créer des connexions en utilisant le driver préalablement chargé. Cette classe dispose d'une méthode statique getConnection() prenant en paramètre l'URL de connexion, le nom d'utilisateur et le mot de passe.

 URL de la forme :

jdbc:<sous-protocole>:<nom-BD>;param=valeur, ...

 L’ URL spécifie :

o

o

o

l'utilisation de JDBC (protocole)

le driver ou le type du SGBDR (sous-protocole)

le nom de la base locale ou distante avec des paramètres de configuration éventuels : nom utilisateur, mot de passe, …

Exemple d’URL

jdbc:odbc:ma_base jdbc:pg95:mabase?username=toto:password=titi

 Ouverture de la connexion :

Connection con = DriverManager.getConnection(url, "myLogin", "myPassword");

 La méthode getConnection() peut

lever une exception de

la classe

java.sql.SQLException.

Mme CHALOUAH Anissa

Page | 7

Exemple

String url = "jdbc:mysql://localhost/tp"; try {

Connection con = DriverManager.getConnection(url, "root", "secret");

} catch (SQLException e) {

System.err.println("Error opening SQL connection: " + e.getMessage()); }

c. Etape 3 : Etablir une requête SQL

Il y a 3 types de requêtes :

 Statement pour les ordres SQL simple. Ces états sont construits par la méthode

createStatement appliquée à la connexion.

PreparedStatement pour les ordres SQL paramétrés. Ces états sont construits par la méthode prepareStatement appliquée à la connexion.

 CallableStatement pour les procédures ou fonctions cataloguées (PL/SQL, C, Java, ..). Ces états sont construits par la méthode prepareCall appliquée à la connexion.

Figure : Connexion et requêtes

 S’il ne doit plus être utilisé dans la suite du code Java, chaque objet de type Statement, PreparedStatement ou CallableStatement devra être fermé à l’aide de la méthode close.

Exemple avec Statement

try {

Statement stmt = conn.createStatement(); }

catch (SQLException e) {

System.err.println("Error creating SQL statement: " + e.getMessage()); } Remarque

Il n'est pas nécessaire de définir un objet Statement pour chaque ordre SQL : il est possible d'en définir un et de le réutiliser

Mme CHALOUAH Anissa

Page | 8

d. Etape 4 : Exécution d'une requête de type Statement

Publicité

Il y a 3 types d'exécutions :

 executeQuery : pour les requêtes (SELECT) accessibles par la méthode Statement.executeQuery(). Cette méthode retourne un résultat de type java.sql.ResultSet contenant les lignes sélectionnées.

executeUpdate : pour les requêtes INSERT, UPDATE, DELETE, CREATE TABLE et DROP TABLE accessibles par la méthode Statement.executeUpdate(). Cette méthode retourne un résultat de type int correspondant au nombre de lignes affectées par la requête.

 execute : pour quelques cas rares (procédures stockées)

Exemple avec executeQuery

String query = "SELECT CIN, name,email FROM users;"; try {

ResultSet resultSet = stmt.executeQuery(query);

} catch (SQLException e) {

System.err.println("Error executing query: " + e.getMessage());

}

Exemple avec executeUpdate

String query = "UPDATE users SET email='[email protected]' WHERE nom='SMITH';"; try {

int result = stmt.executeUpdate(query);

} catch (SQLException e) {

System.err.println("Error executing query: " +

e.getMessage()); }

int count = stmt.executeUpdate( "DELETE FROM ENTREPRISES WHERE CODEPOST LIKE '77%'"); System.out.println("Il y a eu " + count + " lignes supprimées.");

e. Etape 5 : Traitement des résultats

 L'objet ResultSet permet d'avoir un accès aux données résultantes de notre requête en mode ligne par ligne. La méthode ResultSet.next() permet de passer d'une ligne à la suivante. Cette méthode renvoie false dans le cas où il n'y a pas de ligne suivante. Il est nécessaire d'appeler au moins une fois cette méthode, le curseur est placé au départ avant la première ligne (si elle existe).

Mme CHALOUAH Anissa

Page | 9

 La

classe ResultSet dispose aussi d'un

certain nombres d'accesseurs (ResultSet.getXXX()) qui permettent de récupérer le résultat contenu dans une colonne sélectionnée.

On peut utiliser soit le numéro de la colonne désirée, soit son nom avec l'accesseur. La numérotation des colonnes commence à 1.

 XXX correspond au type de la colonne. Le tableau suivant précise les relations entre type

SQL, type JDBC et méthode à appeler sur l'objet ResultSet.

Types

char varchar integer double, float Date

Blob

Type SQL

Type JDBC

Méthode d'accès

String String Integer Double Float Date

Blob

getString() getString() getInt() getDouble() getDouble() getDate()

getBlob()

 La méthode getString() permet d'obtenir la valeur d'un champ de n'importe quel

type.

Exemple

ResultSet rs = stmt.executeQuery("SELECT * FROM users"); try { while (rs.next()) {

System.out.println(rs.getInt("CIN")+" – " +

rs.getString("NOM") +" – " + rs.getString("EMAIL"));

} }

catch (SQLException e) {

System.err.println("Error browsing query results: " +

e.getMessage()); }

ResultSet rs = stmt.executeQuery("SELECT * FROM users"); // Pour accéder à chacun des tuples du résultat de la requête : … while (rs.next()) {

int cin=rs.getInt(1) ; String nom = rs.getString(2); String prenom = rs.getString(3); java.sql.Date date_nais = rs.getDate(4);

... }

Mme CHALOUAH Anissa

Page | 10

f. Etape 6 : Fermeture des différents espaces

 Pour terminer proprement un traitement, il faut fermer les différents espaces ouverts

sinon le garbage collector s’en occupera mais moins efficace

 Chaque objet possède une méthode close() :

 resultset.close();

 statement.close();

 connection.close();

Exemple de programme :

Le programme ci-dessous utilise une base nommée Refugedb . Cette base de données contient une table nommée ANIMAL; voici un script de création :

CREATE TABLE ANIMAL (

id INTEGER PRIMARY KEY, categorie VARCHAR NOT NULL, nom VARCHAR, race VARCHAR, sexe CHAR, date_nais DATE, id_proprio INTEGER, present BIT

) INSERT INTO ANIMAL VALUES (1,'CRM', 'kiki','berger','M','2000-2-21',21,false) INSERT INTO ANIMAL VALUES (2,'CRM','rex','caniche','M','1996-12-2',11,true) ...

Lorsque l'on accède à une base de données, une gestion des exceptions s'avère nécessaire car de multiples problèmes peuvent survenir : le pilote ne peut être chargé (introuvable ?), connexion refusée, requête SQL mal formée... Voici l'exemple complet.

1 import java.sql.*; 2 3 public class TestAnimal { 4 public void test() { 5 final String driver = "org.hsqldb.jdbcDriver"; 6 final String url = "jdbc:hsqldb:/home/kpu/Refuge/Refugedb"; 7 final String user = "sa"; 8 final String password =""; 9 10 Statement st = null; 11 Connection con = null; 12 ResultSet rs = null; 13 String sql = ""; 14 try { 15 Class.forName(driver).newInstance(); 16 con = DriverManager.getConnection(url, user, password); 17 st = con.createStatement(); 18 sql = "SELECT * FROM ANIMAL";

Mme CHALOUAH Anissa

Page | 11

19 rs = st.executeQuery(sql); 20 System.out.println("ID\tTYPE\tNOM\t\tRACE\t"); 21 while (rs.next()) { 22 System.out.print(rs.getInt(1)+"\t"); 23 // ATTENTION, les indices commencent à 1. 24 System.out.print(rs.getString(2)+"\t"); 25 System.out.print(rs.getString("nom")+"\t\t"); 26 System.out.println(rs.getString("race")+"\t"); 27 }//while 28 } 29 catch (ClassNotFoundException e) { 30 System.err.println("Classe non trouvée : " + driver ); 31 } 32 catch (SQLException e) { 33 System.err.println("SQL erreur : "+ sql + " " + e.getMessage()); 34 } 35 catch (Exception e) { 36 System.err.println("Erreur : "+ e); 37 } 38 39 finally { 40 try { if (con != null) { con.close(); } } 41 catch (Exception e) { System.err.println(e); } 42 } 43 } 44 static public void main(String[] arg) { 45 TestAnimal app = new TestAnimal(); 46 app.test(); }}

Figure : Schéma récapitulatif de la mise en œuvre JDBC

Mme CHALOUAH Anissa

Page | 12

3.3.

Accès aux méta-données

JDBC permet de récupérer des informations :

sur le type de données que l'on vient de récupérer par un SELECT (interface ResultSetMetaData)

 mais aussi sur la base elle-même (interface DatabaseMetaData)

a. Interface ResultSetMetaData

 La méthode getMetaData() permet d’obtenir des informations sur les types de

données du ResultSet

 Elle renvoie des instances de ResultSetMetaData 

on peut connaître entre autres :

le nombre de colonne : getColumnCount() o le nom d’une colonne : getColumnName(int col) o le nom de la table : getTableName(int col) o o le type de donnée SQL de la colonne : int getColumnType(int) o si un NULL SQL peut être stocké dans une colonne : isNullable()

Exemple

ResultSet rs = st.executeQuery("SELECT * FROM users"); ResultSetMetaData rsmd = rs.getMetaData(); int nbColonnes = rsmd.getColumnCount(); for (int i = 1; i <= nbColonnes; i++) {

String typeColonne = rsmd.getColumnType(i); String nomColonne= rsmd.getColumnName(i); System.out.println("Colonne " + i+ " de nom " + nomColonne + " de type " + typeColonne);

}

Publicité

b. Interface DatabaseMetaData

Elle permet de donner des informations sur la base de données  Méthode getMetaData() de l’objet Connection  Dépend du SGBD avec lequel on travaille  Elle renvoie des instances de DatabaseMetaData  on peut connaître entre autres :

o o

les tables de la base : getTables() le nom de l’utilisateur : getUserName()

Exemple

private DatabaseMetaData metaData; private java.awt.List listTables= new List(10); … String[] types = { "TABLE", "VIEW" }; String nomTables; … metaData = conn.getMetaData(); ResultSet rs = metaData.getTables(null, null, "%", types);

Mme CHALOUAH Anissa

Page | 13

while (rs.next()) { nomTable = rs.getString(3); listTables.add(nomTable); }

3.4.

Requêtes précompilés (PreparedStatement)

 L'interface PreparedStatement définit les méthodes pour un objet qui va encapsuler une requête précompilée. Ce type de requête est particulièrement adapté pour une exécution répétée d'une même requête avec des paramètres différents.

 Cette interface hérite de l'interface Statement.  Lors de l'utilisation d'un objet de type PreparedStatement, la requête est envoyée au

moteur de la base de données pour que celui ci prépare son exécution.

 Un objet qui implémente l'interface PreparedStatement est obtenu en utilisant la méthode prepareStatement() d'un objet de type Connection. Cette méthode attend en paramètre une chaîne de caractères contenant la requête SQL. Dans cette chaine, chaque paramètre est représenté par un caractère ?

Exemple

PreparedStatement ps = conn.prepareStatement("SELECT * FROM users "+ "WHERE nom = ? ");

 Un ensemble de méthode setXXX(int, valeur) (ou XXX représente un type primitif ou certains objets tel que String, Date, Object, ...) permet de fournir les valeurs de chaque paramètre défini dans la requête.

o

Le premier paramètre précise le numéro du paramètre dont la méthode va fournir la valeur.

o Le second paramètre précise cette valeur.

Exemple

PreparedStatement pstmt = conn.prepareStatement( "UPDATE emp SET sal = ? " + "WHERE grade = ?"); for (int i=0; i<10; i++) {

pstmt.setDouble(1,employe[i].getSalaire()); pstmt.setString(2, employe[i].getNom()); int count = pstmt.executeUpdate();

} Exemple

import java.sql.*; public class TestJDBC2 {

private static void arret(String message) {

System.err.println(message); System.exit(99);}

Mme CHALOUAH Anissa

Page | 14

public static void main(java.lang.String[] args) {

Connection con = null; ResultSet resultats = null; String requete = "";

try {

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch (ClassNotFoundException e) { arret("Impossible de charger le pilote jdbc:odbc"); }

System.out.println("connexion a la base de données");

try { String DBurl = "jdbc:odbc:testDB"; con = DriverManager.getConnection(DBurl); PreparedStatement recherchePersonne = con.prepareStatement("SELECT * FROM personnes WHERE nom_personne = ?"); recherchePersonne.setString(1, "Ali"); resultats = recherchePersonne.executeQuery(); affiche("parcours des données retournées"); boolean encore = resultats.next();

while (encore) { System.out.print(resultats.getInt(1) + " : "+resultats.getString(2)+" "+ resultats.getString(3)+"("+resultats.getDate(4)+")"); System.out.println(); encore = resultats.next();

}

resultats.close();

} catch (SQLException e) { arret(e.getMessage()); } System.out.println("fin du programme"); System.exit(0);

} }

3.5.

Procédure stockée (CallableStatement)

Rappel

 Les procédures stockées permettent de fournir la même fonctionnalité à plusieurs

utilisateurs

 Les procédures sont stockées dans la base côté serveur

JDBC offre une interface dédiée  CallableStatement  dérive de l’interface PreparedStatement  gère le retour de valeur avec ResultSet

Mme CHALOUAH Anissa

Page | 15

Comment création d’un objet CallableStatement   en utlisant Connection.prepareCall(…)

La requête doit être formatée

 

encadrée par des accolades {…} utilisation du préfixe call

Trois modes pour l'appel

la procédure renvoie une valeur

{ ? = call nomProcédure(?,?,…)}

la procédure ne renvoie aucune valeur

{ call nomProcédure(?,?,…)}

si on ne lui passe aucun paramètre

{ call nomProcédure }

Passage de paramètres possible comme pour PreparedStatement Exemple

Sans valeur de retour

CallableStatement testCall; testCall = conn.prepareCall("{ call setSalary(?,?) }"); testCall.setString(1, "€ EUR"); testCall.setLong(2, 2000); testCall.execute();

Un PreparedStatement peut retourner des données grâce à un ResultSet

 Les procédures stockées étendent le modèle

o appel de la procédure précédé du passage des paramètres in et out grâce aux

o

méthodes setXXX() type des paramètres out et in/out grâce à la méthode registerOutParameter()

o exécution de la requête grâce à executeQuery(), executeUpdate() ou

execute() récupération des paramètres out et in/out grâce aux méthodes getXXX()

o

Exemple

On considère la procédure stockée

create or replace procedure augmentation (unDept in integer, pourcentage in number, cout out number) is begin update emp set sal = sal * (1 + pourcentage / 100) where dept = unDept;

select sum(sal) * pourcentage / 100 into cout

Mme CHALOUAH Anissa

Page | 16

from emp where dept = unDept; end;

CallableStatement csmt = conn.prepareCall( "{ call augmentation(?, ?, ?) }"); // 2 chiffres après la virgule pour 3ème paramètre csmt.registerOutParameter(3, Types.DECIMAL, 2); // Augmentation de 2,5 % des salaires du dept 10 csmt.setInt(1, 10); csmt.setDouble(2, 2.5); csmt.executeQuery(); double cout = csmt.getDouble(3); System.out.println("Cout total augmentation : " + cout);

Elles peuvent retourner n'importe quel type de données y compris des ResultSet

 utiliser executeQuery() au lieu de execute() 

récupération d'un ResultSet normal o utilisable comme d'habitude o permet de sauvegarder la requête SQL au niveau de la base

CallableStatement csmt; csmt = myConn.prepareCall("{ call getDetails(?) }"); ResultSet rs; csmt.setLong(1, 1000000); rs = csmt.executeQuery();

Mme CHALOUAH Anissa

Page | 17