Aller au contenu principal

JDBC

Accès à une base de données

Notions théoriques

Qu'est-ce que JDBC ?

JDBC (Java Database Connectivity) est l'API standard Java pour se connecter à une base de données relationnelle. Elle permet d'exécuter des requêtes SQL depuis un programme Java, quel que soit le système de base de données (MySQL, PostgreSQL, SQLite...).

Pour utiliser MySQL, il faut ajouter le driver MySQL Connector/J en dépendance Maven :

<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
</dependency>

Se connecter avec DriverManager.getConnection()

import java.sql.Connection;
import java.sql.DriverManager;

String url = "jdbc:mysql://localhost:3306/ma_base";
String user = "root";
String password = "monMotDePasse";

Connection conn = DriverManager.getConnection(url, user, password);

L'URL de connexion JDBC suit le format : jdbc:mysql://hôte:port/nomBase

PreparedStatement — Les requêtes paramétrées

Injection SQL : ne jamais concaténer des valeurs dans une requête

Construire une requête SQL par concaténation de String est dangereux :

// DANGEREUX - injection SQL possible !
String sql = "SELECT * FROM eleves WHERE nom = '" + nomSaisi + "'";

Si nomSaisi contient ' OR '1'='1, la requête retournera tous les enregistrements. Utilise toujours PreparedStatement avec des ? comme paramètres :

import java.sql.PreparedStatement;
import java.sql.ResultSet;

String sql = "SELECT * FROM eleves WHERE nom = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, nomSaisi); // paramètre 1 = premier ?
ResultSet rs = stmt.executeQuery();

Lire les résultats avec ResultSet

ResultSet est un curseur qui parcourt les lignes retournées par une requête SELECT :

while (rs.next()) {
int id = rs.getInt("id");
String nom = rs.getString("nom");
double note = rs.getDouble("note");
System.out.println(id + " - " + nom + " : " + note);
}
  • rs.next() passe à la ligne suivante et retourne false quand il n'y en a plus
  • rs.getString("colonne"), rs.getInt("colonne"), rs.getDouble("colonne") : lecture par nom de colonne

Insérer et mettre à jour avec executeUpdate()

Pour les requêtes INSERT, UPDATE, DELETE :

String sql = "INSERT INTO eleves (nom, note) VALUES (?, ?)";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, "Alice");
stmt.setDouble(2, 15.5);
int nbLignesAffectees = stmt.executeUpdate();
System.out.println(nbLignesAffectees + " ligne(s) insérée(s).");

Fermer les ressources avec try-with-resources

Les connexions, statements et result sets sont des ressources à fermer :

String url = "jdbc:mysql://localhost:3306/ma_base";

try (Connection conn = DriverManager.getConnection(url, "root", "pass");
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM eleves");
ResultSet rs = stmt.executeQuery()) {

while (rs.next()) {
System.out.println(rs.getString("nom"));
}

} catch (Exception e) {
System.out.println("Erreur SQL : " + e.getMessage());
}
// conn, stmt et rs sont fermés automatiquement

Exemple pratique

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class GestionEleves {

private static final String URL = "jdbc:mysql://localhost:3306/lycee";
private static final String USER = "root";
private static final String PASS = System.getenv("DB_PASSWORD");

public static void listerEleves() {
String sql = "SELECT id, nom, note FROM eleves ORDER BY note DESC";

try (Connection conn = DriverManager.getConnection(URL, USER, PASS);
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {

System.out.println("=== Liste des élèves ===");
while (rs.next()) {
System.out.printf("%-20s %.1f%n",
rs.getString("nom"),
rs.getDouble("note"));
}

} catch (Exception e) {
System.out.println("Erreur : " + e.getMessage());
}
}

public static void ajouterEleve(String nom, double note) {
String sql = "INSERT INTO eleves (nom, note) VALUES (?, ?)";

try (Connection conn = DriverManager.getConnection(URL, USER, PASS);
PreparedStatement stmt = conn.prepareStatement(sql)) {

stmt.setString(1, nom);
stmt.setDouble(2, note);
int nb = stmt.executeUpdate();
System.out.println(nb + " élève(s) ajouté(s).");

} catch (Exception e) {
System.out.println("Erreur insertion : " + e.getMessage());
}
}

public static void main(String[] args) {
ajouterEleve("Alice", 15.5);
listerEleves();
}
}
astuce

Utilise des constantes private static final pour l'URL, le user et le mot de passe (ou mieux : lis-les depuis des variables d'environnement comme dans l'exemple). Cela évite de répéter ces valeurs dans chaque méthode.

Test de mémorisation/compréhension


Que signifie JDBC ?


Pourquoi faut-il utiliser PreparedStatement plutôt que de concaténer des valeurs dans une requête SQL ?


Quelle méthode utilise-t-on pour exécuter un SELECT avec PreparedStatement ?


Quelle méthode de ResultSet passe à la ligne suivante et retourne false s'il n'y en a plus ?


Que retourne stmt.executeUpdate() pour un INSERT ?


Quel est le format de l'URL de connexion JDBC pour MySQL ?


TP pour réfléchir et résoudre des problèmes

Vous allez créer une classe DaoEleve (DAO = Data Access Object) qui centralise les accès à la table eleves.

La table SQL à créer :

CREATE TABLE eleves (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
note DOUBLE
);

Étape 1 — Méthode de connexion

Crée une méthode privée qui retourne une Connection en lisant les paramètres depuis des variables d'environnement.


Bonne pratique - Méthode de connexion centralisée

Centralise la création de connexion dans une seule méthode privée. Si vous changez de base de données ou de mot de passe, vous n'avez qu'un seul endroit à modifier. Le pattern DAO (Data Access Object) applique ce principe à toutes les opérations sur une table.

Étape 2 — Méthode de lecture (SELECT)

Crée une méthode qui retourne tous les élèves depuis la base de données.


Bonne pratique - Fermer ResultSet, Statement et Connection

try-with-resources avec plusieurs ressources les ferme dans l'ordre inverse de déclaration : d'abord rs, puis stmt, puis conn. C'est l'ordre correct car une ressource peut dépendre de la précédente.

Étape 3 — Méthode d'insertion (INSERT)

Crée une méthode qui insère un nouvel élève en utilisant un PreparedStatement.


Bonne pratique - Utiliser le bon setter selon le type

Toujours utiliser setString pour les String, setInt pour les int, setDouble pour les double, etc. N'utilise pas setString pour tout : JDBC doit connaître le type pour construire correctement la requête paramétrée et protéger contre les injections.

📌 Une solution