Gérer une base de données avec JDBC
Gérer une base de données avec JDBC
Achref El Mouelhi
[Link]@[Link]
1 Introduction
2 Mise en place
3 Utilisation
4 Transactions
5 Restructuration du code
Classes connexion et DAO
DataSource et fichier de propriétés
JDBC
L MO
r e f E
A ch
©
JDBC
L MO
r e f E
A ch
JDBC ? ©
API (interface d’application) créée par Sun Microsystems
Permettant de communiquer avec les bases de données
JDBC
JDBC
API de JSE
JDBC
H I ©
EL
Multi-base de données Pas de driver universel
asynchrone
ch r
Fonctionnant en synchrone et
e Complexe
©A
Pas besoin de convertir les données
JDBC
JDBC
H I ©
Aller à https: UEL
O
f E LM
//[Link]/downloads/connector/j/?os=26
r e
Télécharger et Décompresser l’archive .zip
ch
©A
JDBC
H I ©
UEL
O
f E LM
ch r e
©A
Ou aussi
H I ©
U EL
Faire clic droit sur le projet dans Package Explorer et aller dans Properties
O
LM
Properties
f E
Dans Java Build Path, aller dans l’onglet Libraries
r e
ch
©A
Cliquer sur Add JARs
Appliquer
Ou aussi
H I ©
U EL
Faire clic droit sur le projet dans Package Explorer et aller dans Properties
O
LM
Properties
f E
Dans Java Build Path, aller dans l’onglet Libraries
r e
ch
©A
Cliquer sur Add JARs
Appliquer
JDBC
Avant de commencer, voici le script SQL qui permet de créer la base de
données utilisée dans ce cours
CREATE DATABASE cours_jdbc;
USE cours_jdbc;
H I ©
EL
CREATE TABLE personne (
num INT PRIMARY KEY AUTO_INCREMENT,
O U
LM
nom VARCHAR(30),
prenom VARCHAR(30)
)ENGINE=InnoDB;
r e f E
ch
SHOW TABLES;
©A
INSERT INTO personne (nom, prenom) VALUES
("Wick", "John"),
("Dalton", "Jack");
JDBC
Trois étapes
H I ©
Charger le driver JDBC (pour MySQL dans
U L cas)
Enotre
L
Établir la connexion avec la baseMdeOdonnées
r
Créer et exécuterhdese fE
c requêtes SQL
©A
JDBC
H I ©
Avant de commencer
UEL
O
f E LM
Tous les imports de ce chapitre sont de [Link].*;
ch r e
©A
JDBC
Chargement du driver 5
try {
[Link]("[Link]");
}
catch (ClassNotFoundException e) {
[Link]([Link]());
H I ©
}
UEL
O
f E LM
Ou
ch r e
try { ©A
[Link](new [Link]());
}
catch (ClassNotFoundException e) {
[Link]([Link]());
}
JDBC
Chargement du driver 8
try {
[Link]("[Link]");
}
catch (ClassNotFoundException e) {
[Link]([Link]());
H I ©
}
UEL
O
f E LM
Ou
ch r e
try {
©A
[Link](new [Link]()
);
}
catch (ClassNotFoundException e) {
[Link]([Link]());
}
JDBC
Explication
f E LM
r e
port : port TCP/IP utilisé par MySQL (par défaut est 3306)
JDBC
Connexion à la base
f E LM
finally {
if (connexion != null)
ch r e
try {
©A
[Link]();
} catch (SQLException ignore) {
[Link]();
}
}
JDBC
Quelques paramètres à rajouter à la chaı̂ne de connexion pour résoudre les problèmes suivants
H I
Problème avec la demande de clé : allowPublicKeyRetrieval=True©
U EL
Problème de base de données inexistante : createDatabaseIfNotExist=true
O
Problème avec les lettres accentuées :
f E LM
ch r e
characterEncoding=UTF-8&useUnicode=yes
©A
JDBC
Quelques paramètres à rajouter à la chaı̂ne de connexion pour résoudre les problèmes suivants
H I
Problème avec la demande de clé : allowPublicKeyRetrieval=True©
U EL
Problème de base de données inexistante : createDatabaseIfNotExist=true
O
Problème avec les lettres accentuées :
f E LM
ch r e
characterEncoding=UTF-8&useUnicode=yes
©A
Exemple
String url =
"jdbc:mysql://localhost:3306/cours_jdbc?serverTimezone=UTC&useSSL=false
&allowPublicKeyRetrieval=True";
JDBC
Préparation et exécution de la requête
// Préparation de la requête
String selectRequest = "SELECT * FROM Personne;";
H I ©
// Exécution de la requête
UEL
O
ResultSet result = [Link](selectRequest);
f E LM
ch r e
©A
JDBC
Préparation et exécution de la requête
// Préparation de la requête
String selectRequest = "SELECT * FROM Personne;";
H I ©
// Exécution de la requête
U EL
O
ResultSet result = [Link](selectRequest);
f E LM
ch r e
On utilise
©A
execute() pour les requêtes de création : CREATE.
JDBC
H I ©
EL
while ([Link]()) {
int num = [Link]("num");
O U
LM
String nom = [Link]("nom");
r e E
String prenom = [Link]("prenom");
f
[Link](num + " " + nom + " " + prenom);
ch
©A
}
JDBC
H I ©
EL
while ([Link]()) {
int num = [Link](1);
O U
LM
String nom = [Link](2);
r e E
String prenom = [Link](3);
f
[Link](num + " " + nom + " " + prenom);
ch
©A
}
JDBC
©
int nbr = [Link](insertRequest);
if (nbr != 0) {
H I
EL
[Link]("insertion réussie");
}
O U
f E LM
ch r e
©A
JDBC
©
int nbr = [Link](insertRequest);
if (nbr != 0) {
H I
EL
[Link]("insertion réussie");
}
O U
f E LM
ch r
La méthode executeUpdate() retourne
e
©A
0 en cas d’échec de la requête d’insertion, et 1 en cas de succès
JDBC
I ©
String insertRequest = "INSERT INTO Personne (nom, prenom) VALUES ('Wick','John');";
H
UEL
// on demande le renvoi des valeurs attribuées à la clé primaire
[Link](insertRequest, Statement.RETURN_GENERATED_KEYS);
O
E
ResultSet resultat = [Link]();
f LM
// on parcourt les valeurs attribuées à l'ensemble de tuples ajoutés
ch r e
// on vérifie s'il contient au moins une valeur
©A
if ([Link]()) {
[Link]("Identifiant généré pour la personne : " + [Link](1));
}
JDBC
Pour éviter les injections SQL, il faut utiliser les requêtes préparées
String request = "INSERT INTO Personne (nom, prenom) VALUES (?, ?);";
PreparedStatement ps = [Link](request, PreparedStatement.
RETURN_GENERATED_KEYS);
[Link](1, "Wick");
H I ©
EL
[Link](2, "John");
[Link]();
ResultSet resultat = [Link]();
O U
LM
if ([Link]()) {
[Link]("Identifiant généré pour la personne : " + [Link](1));
}
r e f E
ch
©A
JDBC
Pour éviter les injections SQL, il faut utiliser les requêtes préparées
String request = "INSERT INTO Personne (nom, prenom) VALUES (?, ?);";
PreparedStatement ps = [Link](request, PreparedStatement.
RETURN_GENERATED_KEYS);
[Link](1, "Wick");
H I ©
EL
[Link](2, "John");
[Link]();
ResultSet resultat = [Link]();
O U
LM
if ([Link]()) {
[Link]("Identifiant généré pour la personne : " + [Link](1));
}
r e f E
ch
©A
Attention à l’ordre des attributs
JDBC
Transactions
H I ©
EL
Ensemble de requête SQL
O U
LM
Appliquant le principe soit tout (toutes les requête SQL) soit rien
e f
Activées par défaut avec MySQL
r E
ch
©A
Pouvant être désactivées et gérées par le développeur
JDBC
H I ©
UEL
O
f E LM
ch r e
©A
JDBC
H I ©
UEL
Pour valider une transaction
O
[Link]();
f E LM
ch r e
©A
JDBC
H I ©
UEL
Pour valider une transaction
O
[Link]();
f E LM
ch r e
© A
Pour annuler une transaction
[Link]();
JDBC
// désactiver l'auto-commit
[Link](false);
H I ©
String request = "INSERT INTO Personne (nom,prenom) VALUES (?,?);";
EL
PreparedStatement ps = [Link](request, PreparedStatement.
RETURN_GENERATED_KEYS);
[Link](1, "Wick");
O U
LM
[Link](2, "John");
[Link]();
// valider l'insertion
r e f E
[Link]();
ch
©A
ResultSet resultat = [Link]();
if ([Link]()) {
[Link]("Identifiant généré pour la personne : " + [Link](1));
}
JDBC
Organisation du code
h r e
java ayant comme attributs les de cette table
c le code correspondant à l’accès aux données
© Atout
Il faut mettre
(de la base de données) dans des nouvelles classes et interfaces
(qui constitueront la couche DAO : Data Access Object)
JDBC
La classe MySqlConnection
package [Link];
import [Link];
import [Link];
H I ©
static {
U EL
O
try {
LM
String url = "jdbc:mysql://localhost:3306/cours_jdbc";
String utilisateur = "root";
String motDePasse = "";
r e f E
ch
[Link]("[Link]");
©A
connexion = [Link](url, utilisateur, motDePasse);
} catch (Exception e) {
[Link]();
}
}
private MySqlConnection() { }
JDBC
La classe Personne
package [Link];
JDBC
L’interface PersonneDao
package [Link];
import [Link];
H I ©
U
import [Link];EL
M O
L
f E { personne);
public interface PersonneDao
r e
Personnehsave(Personne
© Ac remove(Personne personne);
boolean
Personne update(Personne personne);
Personne findById(int id);
List<Personne> getAll();
}
JDBC
I ©
Déclarons une classe PersonneDaoImpl dans [Link]
H
U ELPersonneDao {
public class PersonneDaoImpl implements
L MO
}
r e f E
A ch
©
JDBC
Implémentons la méthode save
@Override
public Personne save(Personne personne) {
Connection c = [Link]();
try {
PreparedStatement ps = [Link]("INSERT INTO personne (nom,
prenom) VALUES (?,?); ", PreparedStatement.RETURN_GENERATED_KEYS);
[Link](1, [Link]());
[Link](2, [Link]());
H I ©
EL
[Link]();
ResultSet resultat = [Link]();
if ([Link]()) {
O U
LM
[Link]([Link](1));
return personne;
}
r
} catch (SQLException e) {
e f E
ch
[Link]();
©A
}
finally {
try {
[Link]();
} catch (SQLException e) {
// TODO Auto-generated catch block
[Link]();
}
}
return null;
}
@Override
public Personne save(Personne personne) {
Connection c = [Link]();
try {
[Link](false);
PreparedStatement ps = [Link]("INSERT INTO personne (nom,
prenom) VALUES (?,?); ", PreparedStatement.RETURN_GENERATED_KEYS);
[Link](1, [Link]());
[Link](2, [Link]());
[Link]();
H I ©
EL
ResultSet resultat = [Link]();
if ([Link]()) {
[Link]();
O U
LM
[Link]([Link](1));
return personne;
}
} catch (SQLException e) {
r e f E
ch
[Link]();
©A
}
finally {
try {
[Link]();
} catch (SQLException e) {
// TODO Auto-generated catch block
[Link]();
}
}
return null;
}
JDBC
La méthode findById
@Override
public Personne findById(int id) {
Personne personne = null;
Connection c = [Link]();
if (c != null) {
try {
String request = "SELECT * FROM personne WHERE num = ?;";
H I ©
EL
PreparedStatement ps = [Link](request);
U
[Link](1, id);
ResultSet r = [Link]();
O
LM
if ([Link]())
personne = new Personne([Link]("num"), [Link]("nom"), r.
r
} catch (SQLException e) {
e f E
getString("prenom"));
ch
[Link]();
©A
} finally {
try {
[Link]();
} catch (SQLException e) {
[Link]();
}
}
}
return personne;
}
package [Link];
import [Link];
import [Link];
H I ©
EL
public static void main(String args []) {
O
PersonneDaoImpl personneDaoImpl = new PersonneDaoImpl(); U
f E LM
Personne personne = new Personne ("Wick", "John");
Personne insertedPersonne = [Link](personne);
ch r e
©A
if (insertedPersonne != null) {
[Link]("personne numéro " + [Link]()
+ " a été insérée");
} else {
[Link]("problème d'insertion");
}
}
}
JDBC
H I ©
EL
Remarque
O U
LM
N’oublions pas d’implémenter les trois autres méthodes de l’interface
PersonneDao.
r e f E
ch
©A
JDBC
H I ©
Nous devons créer autant d’interfaces DAO que tables de la
EL
bases de données
M OU
f E L une seule interface GenericDao
Pour éviter cela, on peut utiliser
avec un type génh e
r que toutes les classes d’accès aux
c érique
© A
données doivent l’implémenter.
JDBC
L’interface générique GenericDao
package [Link];
import [Link];
JDBC
La classe PersonneDaoImpl
package [Link];
r e f E
ch
©A
JDBC
La classe PersonneDaoImpl
package [Link];
r e f E
ch
©A
Le reste du code est le même.
JDBC
f E LM
ch r e
Créer une nouvelle classe (DataSourceFactory) qui va lire et
construire les différentes propriétés de la connexion
©A
Utiliser DataSourceFactory dans MySqlConnection
JDBC
I ©
Le fichier [Link] situé à la racine du projet (ayant la forme clé = valeur, le
H
EL
nom de la clé est à choisir par l’utilisateur)
O
url=jdbc:mysql://localhost:3306/cours_jdbc?serverTimezone=UTC U
username=root
password=root
f E LM
ch r e
©A
JDBC
Créons la classe MyDataSourceFactory dans [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
H I ©
public class MyDataSourceFactory {
U EL
O
LM
public static DataSource getMySQLDataSource() {
E
Properties props = new Properties();
FileInputStream fis = null;
r e f
MysqlDataSource mysqlDataSource = null;
try {
ch
©A
fis = new FileInputStream("[Link]");
[Link](fis);
mysqlDataSource = new MysqlDataSource();
[Link]([Link]("url"));
[Link]([Link]("username"));
[Link]([Link]("password"));
} catch (IOException e) {
[Link]();
}
return mysqlDataSource;
}
}
JDBC
Remarque
H I ©
U EL
Dans MyDataSourceFactory, on ne précise pas le driver
O
f E LM
[Link] car on utilise un objet de la classe
ch r e
MysqlDataSource qui charge lui même le driver.
©A
import [Link];
import [Link];
import [Link];
H I ©
EL
private MySqlConnection() {
O U
LM
DataSource dataSource = [Link]();
try {
e f E
connexion = [Link]();
r
ch
} catch (SQLException e) {
©A
[Link]();
}
}
public static Connection getConnection() {
if (connexion == null) {
new MySqlConnection();
}
return connexion;
}
import [Link];
import [Link];
H I ©
public static void main(String args []) {
U EL
O
f E LM
PersonneDaoImpl personneDaoImpl = new PersonneDaoImpl();
Personne personne = new Personne ("Wick", "John");
ch r e
Personne insertedPersonne = [Link](personne);
©A
if (insertedPersonne != null)
[Link]("personne numéro " + insertedPersonne.
getNum() + " a été insérée");
else
[Link]("problème d'insertion");
}
}
JDBC
H I ©
EL
Rappel
O U
LM
Notre approche est idéale pour une application de petite taille ou
mono-utilisateur.
r e f E
ch
©A
JDBC
Problématique
r e
h ée par des threads.
ne peut êtrecpartag
© A
JDBC
Problématique
r e
h ée par des threads.
ne peut êtrecpartag
© A
JDBC
e f E
Le pool de connexions se charge de retourner un objet Connection aux
r
ch
méthodes de l’application qui la demandent.
©A
Le client qui appelle la méthode [Link] perd la connexion sans la
fermer réellement. La connexion sera libérée et pourra être redistribuée de
nouveau.
JDBC
Techniquement, comment faire ?
H I ©
UEL
O
f E LM
ch r e
©A
JDBC
Techniquement, comment faire ?
H I ©
UEL
O
LM
Exemples d’implémentation de Connection pool pour Java
HikariCP
r e f E
ch
©A
BoneCP
DBPool
Apache DBCP
c3p0
...
JDBC
HikariCP, pourquoi ?
Plus performant
Plus utilisé
H I ©
Écrit en Java UEL
O
...
f E LM
ch r e
©A
JDBC
HikariCP, pourquoi ?
Plus performant
Plus utilisé
H I ©
Écrit en Java UEL
O
...
f E LM
ch r e
©A
Dépôt GitHub
[Link]
JDBC
Aller à [Link]
H I ©
zaxxer/HikariCP/5.0.1
UEL
O
Télécharger et Décompresser l’archive
f E LM
r e
Déplacer les deux fichiers .jar (HikariCP et slf4j) dans le
ch
A
dossier lib du projet
©
Ajouter les deux fichiers au build path du projet
JDBC
url=jdbc:mysql://localhost:3306/cours_jdbc?serverTimezone=UTC
username=root
H I ©
EL
password=root
O U
f E LM
Par jdbcUrl
ch r e
©A
jdbcUrl=jdbc:mysql://localhost:3306/cours_jdbc?serverTimezone=UTC
username=root
password=root
JDBC
H I ©
public class DataSource {
UEL
L M{O
r e f E
private DataSource()
} A ch
©
}
JDBC
package [Link];
import [Link];
import [Link];
H I ©
UEL
public class DataSource {
O
private static HikariDataSource ds;
f E LM
r e
private static HikariConfig conf = new HikariConfig("[Link]");
ch
©A
private DataSource() {
JDBC
Définissons une méthode getConnection() qui retournera un objet Connection
package [Link];
import [Link];
import [Link];
import [Link];
H I ©
import [Link];
UEL
O
LM
public class DataSource {
r e
private static HikariDataSource ds;f E
ch
private static HikariConfig conf = new HikariConfig("[Link]");
©A
private DataSource() { }
import [Link];
import [Link];
H I ©
public static void main(String args []) {
UEL
O
f E LM
PersonneDaoImpl personneDaoImpl = new PersonneDaoImpl();
Personne personne = new Personne ("Wick", "John");
ch r e
Personne insertedPersonne = [Link](personne);
©A
if (insertedPersonne != null)
[Link]("personne numéro " + insertedPersonne.
getNum() + " a été insérée");
else
[Link]("problème d'insertion");
}
}
JDBC
valeur souhaitée
H I ©
Pour fixer le nombre de connexion de la pool, on ajoute la clé maximumPoolSize avec la
UEL
O
jdbcUrl=jdbc:mysql://localhost:3306/cours_jdbc?serverTimezone=UTC
LM
username=root
password=root
maximumPoolSize=10
r e f E
ch
©A
JDBC
Considérons la classe Adresse suivante
package [Link];
r e f E
ch
public Adresse(Integer id, String rue, String codePostal, String
ville) {
[Link] = id;
[Link] = rue;
©A
[Link] = codePostal;
[Link] = ville;
}
JDBC
r e f E
ch
private List<Adresse> adresses;
©A
// + getter / setter / toString
INSERT INTO personne (nom, prenom) VALUES ("Wick", "John"), ("Dalton", "Jack");
H I ©
CREATE TABLE adresse(
id INT PRIMARY KEY AUTO_INCREMENT,
U EL
rue VARCHAR(30),
O
LM
code_postal VARCHAR(30),
ville VARCHAR(30)
)ENGINE=InnoDB;
r e f E
ch
INSERT INTO adresse (rue, code_postal, ville) VALUES
©A
("paradis", "13006", "Marseille"),
("plantes", "75014", "Paris");
INSERT INTO personne_adresse (num_personne, id_adresse) VALUES (1, 1), (1, 2), (2, 2);
JDBC
Exercice 1
H I ©
Créez une classe AdresseDao qui implémente Dao
UEL
O
Implémentez les méthodes de Dao
f E LM
r e
Dans main, testez toutes les méthodes implémentées dans
ch
©A
AdresseDao.
JDBC
JDBC
ch r e
©A