Cours Java JDBC
Cours Java JDBC
Achref El Mouelhi
elmouelhi.achref@gmail.com
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
//dev.mysql.com/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 java.sql.*;
ch r e
©A
JDBC
Chargement du driver 5
try {
Class.forName("com.mysql.jdbc.Driver");
}
catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
H I ©
}
UEL
O
f E LM
Ou
ch r e
try { ©A
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
}
catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
}
JDBC
Chargement du driver 8
try {
Class.forName("com.mysql.cj.jdbc.Driver");
}
catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
H I ©
}
UEL
O
f E LM
Ou
ch r e
try {
©A
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver()
);
}
catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
}
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
connexion.close();
} catch (SQLException ignore) {
ignore.printStackTrace();
}
}
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 = statement.executeQuery(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 = statement.executeQuery(selectRequest);
f E LM
ch r e
On utilise
©A
execute() pour les requêtes de création : CREATE.
JDBC
H I ©
EL
while (result.next()) {
int num = result.getInt("num");
O U
LM
String nom = result.getString("nom");
r e E
String prenom = result.getString("prenom");
f
System.out.println(num + " " + nom + " " + prenom);
ch
©A
}
JDBC
H I ©
EL
while (result.next()) {
int num = result.getInt(1);
O U
LM
String nom = result.getString(2);
r e E
String prenom = result.getString(3);
f
System.out.println(num + " " + nom + " " + prenom);
ch
©A
}
JDBC
©
int nbr = statement.executeUpdate(insertRequest);
if (nbr != 0) {
H I
EL
System.out.println("insertion réussie");
}
O U
f E LM
ch r e
©A
JDBC
©
int nbr = statement.executeUpdate(insertRequest);
if (nbr != 0) {
H I
EL
System.out.println("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
statement.executeUpdate(insertRequest, Statement.RETURN_GENERATED_KEYS);
O
E
ResultSet resultat = statement.getGeneratedKeys();
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 (resultat.next()) {
System.out.println("Identifiant généré pour la personne : " + resultat.getInt(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 = connexion.prepareStatement(request, PreparedStatement.
RETURN_GENERATED_KEYS);
ps.setString(1, "Wick");
H I ©
EL
ps.setString(2, "John");
ps.executeUpdate();
ResultSet resultat = ps.getGeneratedKeys();
O U
LM
if (resultat.next()) {
System.out.println("Identifiant généré pour la personne : " + resultat.getInt(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 = connexion.prepareStatement(request, PreparedStatement.
RETURN_GENERATED_KEYS);
ps.setString(1, "Wick");
H I ©
EL
ps.setString(2, "John");
ps.executeUpdate();
ResultSet resultat = ps.getGeneratedKeys();
O U
LM
if (resultat.next()) {
System.out.println("Identifiant généré pour la personne : " + resultat.getInt(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
connection.commit();
f E LM
ch r e
©A
JDBC
H I ©
UEL
Pour valider une transaction
O
connection.commit();
f E LM
ch r e
© A
Pour annuler une transaction
connection.rollback();
JDBC
// désactiver l'auto-commit
connexion.setAutoCommit(false);
H I ©
String request = "INSERT INTO Personne (nom,prenom) VALUES (?,?);";
EL
PreparedStatement ps = connexion.prepareStatement(request, PreparedStatement.
RETURN_GENERATED_KEYS);
ps.setString(1, "Wick");
O U
LM
ps.setString(2, "John");
ps.executeUpdate();
// valider l'insertion
r e f E
connexion.commit();
ch
©A
ResultSet resultat = ps.getGeneratedKeys();
if (resultat.next()) {
System.out.println("Identifiant généré pour la personne : " + resultat.getInt(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 org.eclipse.config;
import java.sql.Connection;
import java.sql.DriverManager;
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
Class.forName("com.mysql.cj.jdbc.Driver");
©A
connexion = DriverManager.getConnection(url, utilisateur, motDePasse);
} catch (Exception e) {
e.printStackTrace();
}
}
private MySqlConnection() { }
JDBC
La classe Personne
package org.eclipse.model;
JDBC
L’interface PersonneDao
package org.eclipse.dao;
import java.util.List;
H I ©
U
import org.eclipse.model.Personne;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 org.eclipse.dao
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 = MySqlConnection.getConnection();
try {
PreparedStatement ps = c.prepareStatement("INSERT INTO personne (nom,
prenom) VALUES (?,?); ", PreparedStatement.RETURN_GENERATED_KEYS);
ps.setString(1, personne.getNom());
ps.setString(2, personne.getPrenom());
H I ©
EL
ps.executeUpdate();
ResultSet resultat = ps.getGeneratedKeys();
if (resultat.next()) {
O U
LM
personne.setNum(resultat.getInt(1));
return personne;
}
r
} catch (SQLException e) {
e f E
ch
e.printStackTrace();
©A
}
finally {
try {
c.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
@Override
public Personne save(Personne personne) {
Connection c = MySqlConnection.getConnection();
try {
c.setAutoCommit(false);
PreparedStatement ps = c.prepareStatement("INSERT INTO personne (nom,
prenom) VALUES (?,?); ", PreparedStatement.RETURN_GENERATED_KEYS);
ps.setString(1, personne.getNom());
ps.setString(2, personne.getPrenom());
ps.executeUpdate();
H I ©
EL
ResultSet resultat = ps.getGeneratedKeys();
if (resultat.next()) {
c.commit();
O U
LM
personne.setNum(resultat.getInt(1));
return personne;
}
} catch (SQLException e) {
r e f E
ch
e.printStackTrace();
©A
}
finally {
try {
c.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
JDBC
La méthode findById
@Override
public Personne findById(int id) {
Personne personne = null;
Connection c = MySqlConnection.getConnection();
if (c != null) {
try {
String request = "SELECT * FROM personne WHERE num = ?;";
H I ©
EL
PreparedStatement ps = c.prepareStatement(request);
U
ps.setInt(1, id);
ResultSet r = ps.executeQuery();
O
LM
if (r.next())
personne = new Personne(r.getInt("num"), r.getString("nom"), r.
r
} catch (SQLException e) {
e f E
getString("prenom"));
ch
e.printStackTrace();
©A
} finally {
try {
c.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return personne;
}
package org.eclipse.classes;
import org.eclipse.dao.PersonneDaoImpl;
import org.eclipse.model.Personne;
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 = personneDaoImpl.save(personne);
ch r e
©A
if (insertedPersonne != null) {
System.out.println("personne numéro " + insertedPersonne.getNum()
+ " a été insérée");
} else {
System.out.println("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 org.eclipse.dao;
import java.util.List;
JDBC
La classe PersonneDaoImpl
package org.eclipse.dao;
r e f E
ch
©A
JDBC
La classe PersonneDaoImpl
package org.eclipse.dao;
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 db.properties 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 org.eclipse.config
package org.eclipse.config;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import javax.sql.DataSource;
import com.mysql.cj.jdbc.MysqlDataSource;
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("db.properties");
props.load(fis);
mysqlDataSource = new MysqlDataSource();
mysqlDataSource.setURL(props.getProperty("url"));
mysqlDataSource.setUser(props.getProperty("username"));
mysqlDataSource.setPassword(props.getProperty("password"));
} catch (IOException e) {
e.printStackTrace();
}
return mysqlDataSource;
}
}
JDBC
Remarque
H I ©
U EL
Dans MyDataSourceFactory, on ne précise pas le driver
O
f E LM
com.mysql.jdbc.Driver car on utilise un objet de la classe
ch r e
MysqlDataSource qui charge lui même le driver.
©A
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
H I ©
EL
private MySqlConnection() {
O U
LM
DataSource dataSource = MyDataSourceFactory.getMySQLDataSource();
try {
e f E
connexion = dataSource.getConnection();
r
ch
} catch (SQLException e) {
©A
e.printStackTrace();
}
}
public static Connection getConnection() {
if (connexion == null) {
new MySqlConnection();
}
return connexion;
}
import org.eclipse.dao.PersonneDaoImpl;
import org.eclipse.model.Personne;
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 = personneDaoImpl.save(personne);
©A
if (insertedPersonne != null)
System.out.println("personne numéro " + insertedPersonne.
getNum() + " a été insérée");
else
System.out.println("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 connection.close 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
https://github.com/brettwooldridge/HikariCP
JDBC
Aller à https://jar-download.com/artifacts/com.
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 org.eclipse.config;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
H I ©
UEL
public class DataSource {
O
private static HikariDataSource ds;
f E LM
r e
private static HikariConfig conf = new HikariConfig("db.properties");
ch
©A
private DataSource() {
JDBC
Définissons une méthode getConnection() qui retournera un objet Connection
package org.eclipse.config;
import java.sql.Connection;
import java.sql.SQLException;
import com.zaxxer.hikari.HikariConfig;
H I ©
import com.zaxxer.hikari.HikariDataSource;
UEL
O
LM
public class DataSource {
r e
private static HikariDataSource ds;f E
ch
private static HikariConfig conf = new HikariConfig("db.properties");
©A
private DataSource() { }
import org.eclipse.dao.PersonneDaoImpl;
import org.eclipse.model.Personne;
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 = personneDaoImpl.save(personne);
©A
if (insertedPersonne != null)
System.out.println("personne numéro " + insertedPersonne.
getNum() + " a été insérée");
else
System.out.println("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 com.example.demo.model;
r e f E
ch
public Adresse(Integer id, String rue, String codePostal, String
ville) {
this.id = id;
this.rue = rue;
©A
this.codePostal = codePostal;
this.ville = 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