CHAPITRE II: L’ACCÈS
AUX BASES DE DONNÉES
Partie 1: Introduction à la persistance des objets
ISET Mahdia AU 2014-2015
Généralités sur la persistance des
2
données dans les applications
Lancement, exécution et arrêt d’une application peut
entraîner la perte des données et des états d’un
programme
En programmation, la gestion de la persistance des
données est le mécanisme responsable de la
sauvegarde et la restauration de ces données.
Généralités sur la persistance des
3
données dans les applications
Différentes solutions peuvent être utilisées pour la
persistance des objets en Java :
· Sérialisation
· JDBC : Java Data Base Connectivity
· Génération automatisée de code source
· SQL/J
· Enrichissement du code source (enhancement)
· Génération de bytecode
· framework de mapping O/R (Object Relational
Mapping)
· Base de données objet (ODBMS)
Généralités sur la persistance des
4
données dans les applications
Stockage des données dans des fichiers ou bases de
données
Quels intérêts des BD?
Meilleure gestion du cycle de vie des données
Intégrité référentielle, cohérence par le transactionnel
Architecture distribuée, réseau
Exploitation, maintenance
Les modèles de données
5
Il existe plusieurs types de base de données
Temps Théories ensemblistes
Les
et données
l'algèbre sont
relationnel.
Hiérarchique
organisées
Des tables de
Relationnel façonprimaires
Clés hiérarchique
et
grâce à des
étrangères.
Objet [Link].
Requêtes
Exemple : MySQL,
XML
PosgreSQL, HSQLDB,
Derby
·
CHAPITRE II: L’ACCÈS
AUX BASES DE DONNÉES
Partie 2: L’API JDBC (Java Data Base Connectivity
ISET Mahdia AU 2014-2015
Les classes de L’API JDBC
7
Cette API à était développée par SUN pour permettre à
des applications Java d'accéder à des bases de
données relationnelles quelconques
Toutes les classes de JDBC sont dans le package
[Link]. Il faut donc l'importer dans tous les
programmes devant utiliser JDBC.
Exemple : import [Link].*;
Les classes de L’API JDBC
8
Classe Rôle
DriverManager Charger et configurer le driver de la
base de données.
Connection Réaliser la connexion et
l'authentification à la base de
données.
Statement (et PreparedStatement) Contenir la requête SQL et la
transmettre à la base de données.
ResultSet Parcourir les informations
retournées par la base de données
dans le
cas d'une sélection de données
Etapes d’accès à une BD
9
Première étape:
Préciser le type de driver que l'on veut utiliser. Le Driver
permet de gérer l'accès à un type particulier de SGBD
Deuxième étape:
Récupérer un objet « Connection » en s'identifiant
auprès du SGBD et en précisant le nom de la base de
donnée à utiliser
Etapes d’accès à une BD
10
Etapes suivantes
A partir de la connexion, créer un « statement » (état)
correspondant à une requête particulière
Exécuter ce statement au niveau du SGBD
Fermer le statement
Dernière étape
Se déconnecter de la base en fermant la connexion
Étape 1: charger le pilote
11
Qu’est ce qu’un pilote/driver?
Un pilote ou driver JDBC est un "logiciel" qui permet
d'établir une connexion entre un programme java et un
système de gestion de bases de données.
Ce "logiciel" est en fait une implémentation de l'interface
Driver, du package [Link].
Le pont JDBC-ODBC est un exemple de driver JDBC, ce
dernier permet l'interfaçage avec ODBC (il est contenu
dans J2SE : [Link]).
Étape 1: charger le pilote
12
Qu’est ce qu’un pilote/driver?
II existe 4 types de drivers JDBC qui se différencient par
leur implémentation (notamment par l'utilisation ou non de
méthodes natives)
Les drivers sont pris en charge par la classe
DriverManager.
Étape 1: charger le pilote
13
Les types de drivers
Les drivers JDBC :
Le pont JDBC-ODBC (JDBC-ODBC bridge plus ODBC
driver) : Ce driver est présent dans J2SE
([Link]). Il permet l'acces au
SGBD via le driver ODBC.
Native-API partly-Java driver : Ce type de driver traduit
les appels de JDBC à un SGBD particulier, grâce à un
mélange d'API java et d'API natives.
Étape 1: charger le pilote
14
Les types de drivers
JDBC-Net pure Java driver : Ce type de driver (écrit
entièrement en java) passe par un serveur intermédiaire
pour l'accès au SGBD.
Native-protocol pure Java driver : Ce type de driver
(écrit entièrement en java) se connecte directement au
SGBD.
Étape 1: charger le pilote
15
Les types de drivers
Étape 1: charger le pilote
16
Comment charger un driver?
Utiliser la méthode [Link], qui aura pour effet
d'enregistrer le Driver auprès du DriverManager.
Syntaxe:
String nomDriver = "nom_du_driver";
try{
[Link](nomDriver);
}catch(ClassNotFoundException cnfe)
{ [Link]("La classe "+nomDriver+" n'a pas
été trouvée");
[Link](); }
Étape 1: charger le pilote
17
Comment charger un driver?
En pratique, à cause d'implémentations imparfaites des
spécifications, il sera parfois nécessaire d'utiliser cette
syntaxe :
[Link](nomDriver).newInstance();
Exemples:
[Link]("[Link]");
//pour le pont JDBC-ODBC
[Link]("[Link]");
//pour MySQL et ConnectorJ
Étape 2: Connexion à une BD
18
Une connexion à une base de données avec JDBC
est représentée par une instance de la
classe [Link].
Pour ouvrir une connexion vers une base de
données, il suffit de spécifier l'url de connexion, le
login et le password, à la méthode getConnection
de DriverManager.
Étape 2: Connexion à une BD
19
Syntaxe
Syntaxe:
String url = "url";
String login = "log";
String password = "pass";
try{
Connection connection =
[Link](url,login,password);
} catch(SQLException sqle)
{ // gérer l’erreur }
Finally
{ //fermer la connexion }
Étape 2: Connexion à une BD
20
Structure d’une URL
Les URL JDBC sont définies sous forme de String selon ce
schéma :
String url = "jdbc:<subprotocol>:<subname>"
Avec:
<jdbc>: Le protocole dans une URL JDBC est toujours jdbc
<subprotocol>: Cela correspond au nom du driver ou
au mécanisme de connexion à la base de données.
<subname> : Une manière d'identifier la source de
données. Ce dernier élément dépend complètement du
sous-protocole et du driver.
Étape 2: Connexion à une BD
21
Exemples d’URL
jdbc:odbc:maBase;CacheSize=30;ExtensionCase=LOWER
jdbc:mysql://localhost/maBase
jdbc:oracle:oci8@:maBase
jdbc:oracle:thin@://localhost:8000:maBase
jdbc:sybase:Tds:localhost:5020/maBase
Étape 2: Connexion à une BD
22
Comment fermer une connexion?
La connexion est fermée automatiquement par le garbage
collector, mais il est toujours préférable de fermer soi même
une connexion.
De manière générale il est nécessaire de bien gérer les
exceptions suivantes:
ClassNotFoundException: problème de chargement du
driver (ex: driver introuvable)
SqlException: problème au niveau de la connexion(ex: BD
introuvable)
Étape 2: Connexion à une BD
23
Comment fermer une connexion?
String driver = "[Link]";
String url = "url";
String login = "login« ;
String password = "password";
Connection connection = null;
try{
[Link](driver);
connection =
[Link](url,login,password);
}
Étape 2: Connexion à une BD
24
Comment fermer une connexion?
catch(ClassNotFoundException cne)
{ [Link]("Driver introuvable : ");
[Link](); }
catch(SQLException sqle)
{ [Link]("Erreur SQL : " );}
catch(Exception e)
{ [Link]("Autre erreur : ");
[Link](); }
finally {if(connection!=null)
{try{[Link]()}
catch(Exception e)
{[Link]();}}
Étape 3: Accès à la BD
25
Comment créer un statement?
L'interface Statement représente une instruction SQL.
L'obtention d'une instance de cette interface se fait à partir de
la Connection :
try{
Connection connection = ...
Statement statement = [Link]();
} catch(Exception e)
{ //cf. Comment gérer les erreurs ? }
Étape 3: Accès à la BD
26
Comment exécuter un statement?
Méthode d’exécution Rôle Type de retour
execute Générique pour un boolean
true si l'instruction renvoie
n'importe quelle un ResultSet, false sinon
expression SQL.
executeQuery SELECT un ResultSet contenant les
résultats
executeUpdate INSERT, UPDATE, int indiquant le nombre de
tuples (lignes) modifiés
DELETE, CREATE,
etc
exécuteBatch Spécifique aux int[] : un tableau d'entiers
indiquant le nombre de
transactions tuples modifiés pour chaque
commande contenue dans le
batch.
Étape 3: Accès à la BD
27
Quelques exemples
Statement statement = [Link]();
boolean result = [Link]("SELECT * FROM
MATABLE");
ResultSet resultSet = [Link]("SELECT
ATTRIBUT1, ATTRIBUT2 FROM MATABLE");
int col = [Link]("INSERT INTO
MATABLE VALUES(15,'bonjour',7.0)");
Étape 3: Accès à la BD
28
La classe resultSet
C'est une classe qui représente une abstraction d'une
table qui se compose de plusieurs enregistrements
constitués de colonnes qui contiennent les données.
Les principales méthodes pour obtenir des données
sont :
getInt(int/String) : retourne sous forme d'entier le
contenu de la colonne dont le numéro/nom est passé en
paramètre.
getFloat(int/String) : retourne sous forme d'un nombre
flottant le contenu de la colonne dont le numéro/nom est
passé en paramètre.
Étape 3: Accès à la BD
29
La classe resultSet
getDate(int/String) : retourne sous forme de date le
contenu de la colonne dont le numéro/nom est passé en
paramètre.
next()/previous()/last()/First()/beforeFirst()/
afterLast(): déplacer le curseur sur l’enregistrement en
question.
close(): ferme le ResultSet
getMetaData(): retourne un objet de type
ResultSetMetaData associé au ResultSet.
Étape 3: Accès à la BD
30
Le parcours d’un resultSet
Connection connection =
[Link](url,user,password);
Statement statement = [Link]();
ResultSet resultat = [Link]("SELECT *
FROM MaTable");
[Link]([Link]()); //true
[Link](); //on se retrouve ici sur la première ligne
//traitement de la première ligne ...
while([Link]()) { //traitement des autres lignes }
[Link](); //on a replacé ici le curseur sur la première
ligne //etc.
Étape 3: Accès à la BD
31
L’interface ResultSetMetaData
La méthode getMetaData() d'un objet ResultSet retourne
un objet de type ResultSetMetaData. Cet objet permet de
connaître le nombre, le nom et le type des colonnes:
int getColumnCount(): Retourner le nombre de
colonnes du ResultSet
String getColumnName(int): Retourner le nom de la
colonne dont le numéro est donné
String getColumnLabel(int): Retourner le libellé de la
colonne donnée
boolean isReadOnly(int): Retourner true si la colonne
est en lecture seule
Étape 3: Accès à la BD
Qu’est ce qu’un
32
PreparedStatement?
L'interface PreparedStatement étend Statement et
représente une instruction paramétrée. Cette interface
diffère de Statement sur deux points principaux :
Les instances de PreparedStatement contiennent
une instruction SQL dèjà compilée. D'où le
terme prepared. Cela améliore notamment les
performances si cette instruction doit être appelée de
nombreuses fois.
Étape 3: Accès à la BD
Qu’est ce qu’un
33
PreparedStatement?
Les instructions SQL des instances de
PreparedStatement contiennent un ou plusieurs
paramètres d'entrée, non spécifiés lors de la création de
l'instruction. Ces paramètres sont représentés par des
points d'interrogation(?). Ces paramètres doivent être
spécifiés avant l'exécution.
Étape 3: Accès à la BD
34
Création d’un PreparedStatement
PreparedStatement prep1 =
[Link]("SELECT * FROM
Annuaire WHERE nom = ?");
PreparedStatement prep2 =
[Link]("UPDATE Annuaire SET
noTel = ? WHERE nom = ?");
PreparedStatement prep3 =
[Link]("SELECT Attribut1,
Attribut2 FROM MaTable");
Étape 3: Accès à la BD
35
Passage des paramètres
Le passage des paramètres d'entrée des
PreparedStatement se fait grâce à l'ensemble des
méthodes setXXX. Il est important de connaître
les correspondances entre les types SQL et les types
java
Consulter le lien suivant:
[Link]
page=types#tabRelation
Étape 3: Accès à la BD
36
Passage des paramètres
String sql = "UPDATE Stocks SET prix = ?, quantite = ?
WHERE nom = ?";
PreparedStatement preparedStatement =
[Link](sql);
[Link](1,15.6);
[Link](2,256);
[Link](3,"café");
[Link]();
Étape 3: Accès à la BD
37
Appel des procédures stockées
L'interface CallableStatement définit les méthodes pour
un objet qui va permettre d'appeler une procédure
stockée.
Cette interface hérite de l'interface PreparedStatement.
JDBC propose une syntaxe unifiée pour l’appel des
procédures.
Étape 3: Accès à la BD
38
Appel des procédures stockées
Cette syntaxe peut prendre plusieurs formes:
{call nom_procedure_stockees} : cette forme la plus
simple permet l'appel d'une procédure stockée sans
paramètre ni valeur de retour.
{call nom_procedure_stockees(?, ?, ...)} : cette forme
permet l'appel d'une procédure stockée avec des
paramètre
Étape 3: Accès à la BD
39
Appel des procédures stockées
{? = call nom_procedure_stockees(?, ?, ...)} : cette forme
permet l'appel d'une procédure stockée avec des
paramètres et une valeur de retour.
Exemple:
Statement statement = [Link]();
String sql = "{call rechercherNom(?)}";
CallableStatement call = [Link](sql);
//passage de la chaîne "ioio" comme valeur du premier
paramètre
[Link](1,"ioio");
Étape 3: Accès à la BD
40
Appel des procédures stockées
if([Link]())
{ // récupération des ResultSet
ResultSet resultat1 = [Link]();
while([Link]())
{ for(int i=0;i<[Link]().getColumnCount();i+
+)
{ [Link]([Link](i+1)+", "); }
Étape 3: Accès à la BD
41
La gestion de SqlException
La classe SQLException représente les erreurs émises
par la base de données. Elle contient trois attributs qui
permettent de préciser l'erreur :
message : contient une description de l'erreur
SQLState : code défini par les normes X/Open et SQL99
ErrorCode : le code d'erreur du fournisseur du pilote
La classe SQLException possède une méthode
getNextException() qui permet d'obtenir les autres
exceptions levées durant la requête. La méthode renvoie
null une fois la dernière exception renvoyée.
Étape 3: Accès à la BD
42
La gestion de SqlException
Exemple:
try { }
catch (SQLException e)
{ [Link]("SQLException");
do {
[Link]("SQLState : " + [Link]());
[Link]("Description : " + [Link]());
[Link]("code erreur : " + [Link]());
[Link]("");
e = [Link]();
} while (e != null);
Étape 4: Déconnexion
43
La méthode close() permet de libérer les ressources prises
par la création d'objets de type ResultSet, Statement,
et Connection. Elle existe pour chacune de ces interfaces.
Elle est le plus souvent employée pour une Connection,
car elle libère en même temps toutes les ressources qui lui
sont associées.
Il faut commencer par libérer le ResultSet, puis le
Statement et enfin la connection.
CHAPITRE II: L’ACCÈS
AUX BASES DE DONNÉES
Partie 3: La persistance de données avec
DAO (Data Access Object)
ISET Mahdia AU 2014-2015
Les problèmes rencontrés avec la
45
solution JDBC
La première approche pour faire une correspondance entre le
modèle objet et relationnel a été d'utiliser l'API JDBC fournie
en standard avec le JDK. Cependant cette approche possède
plusieurs inconvénients majeurs :
nécessite l'écriture de nombreuses lignes de codes, souvent
répétitives
le mapping entre les tables et les objets est un travail de bas
niveau
Le code pour la persistance selon le type de stockage (BD
relationnelles, BD objet, fichiers simples, etc.) avec les
implémentations des fournisseurs de SGBD
Si le code pour la persistance est imbriquée avec le code «
métier », il est difficile de changer de sources de données
Les problèmes rencontrés avec la
46
solution JDBC
L’utilisation de JDBC a entrainé l’apparition de plusieurs
problèmes:
nécessite l'écriture de nombreuses lignes de codes, souvent
répétitives
le mapping entre les tables et les objets est un travail de
bas niveau
Le code pour la persistance selon le type de stockage (BD
relationnelles, BD objet, fichiers simples, etc.) avec les
implémentations des fournisseurs de SGBD
Si le code pour la persistance est imbriquée avec le code «
métier », il est difficile de changer de sources de données
Solution: Ajouter une couche de
47
persistance
L'accès aux données dans une application multiniveau
doit être encapsulée dans une couche dédiée aux
interactions avec la base de données généralement
appelée couche de persistance. Celle-ci permet
notamment :
d'ajouter un niveau d'abstraction entre la base de
données et l'utilisation qui en est faite.
de simplifier la couche métier qui utilise les traitements
de cette couche
de masquer les traitements réalisés pour mapper les
objets dans la base de données et vice versa
de faciliter le remplacement de la base de données
utilisée
Les opérations de type CRUD
48
L'acronyme CRUD (Create, Read, Update and Delete)
désigne les quatre opérations réalisables sur des
données (création, lecture, mise à jour et suppression).
Create pour créer une nouvelle entité dans la base
Retrieve pour retrouver une ou plusieurs entités de la
base
Update pour modifier une entité de la base
Delete pour supprimer une entité de la base
Le modèle de conception DAO
49
Permet d’encapsuler le code lié à la persistance des
données dans des objets DAO dont l’interface est
indépendante de la source de données
Quand l’application a besoin d’effectuer une opération liée
à la persistance d’un objet (CRUD), elle fait appel à un
objet DAO à qui elle passe les informations nécessaires
pour effectuer l’opération.
Le modèle de conception DAO
50
Chaque classe d’objet métier a son propre type de DAO
(DAOEmploye, DAODepartement, …) Mais le même
objet DAO peut être utilisé pour tous les objets d’une classe
d’objet métier.
Le modèle de conception DAO
51
Exemple: une interface qui propose des opérations de
type CRUD pour un objet de type Employe
public DAOEmploye
{ public Employe obtenir(Integer id);
public void creer(Employe emp);
public void modifier(Employe emp);
public Collection obtenirTous();
public void supprimer(Employe emp); }
Le modèle DAO
52
Les DAOs sont placés dans la couche dite « d’accès aux
données » qui est souvent sur une autre machine que la
couche des objets métiers
Exemple: Le modèle DAO pour
53
l’objet Emlployé