<style>
.slides h1,
.slides h2,
.slides h3,
.slides h4,
.slides h5,
.slides h6,
h1,
h2,
h3,
h4,
h5,
h6{
color: #d35400;
}
.slides {
font-size: 3.3rem;
box-shadow: 0 0 80px #d35400;
}
.slides strong,
.slides em{
color: #FF926B;
}
.slides pre code { width: auto; max-height: initial; }
.slides code{
color: #FFBD96;
font-style: italic;
}
/* Dark theme book */
html, body, .ui-content {
background-color: #333;
color: #ddd;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
color: #ddd;
}
.markdown-body h1,
.markdown-body h2 {
border-bottom-color: #ffffff69;
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: #fff;
}
.markdown-body img {
background-color: transparent;
}
.ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a {
color: white;
border-left: 2px solid white;
}
.expand-toggle:hover,
.expand-toggle:focus,
.back-to-top:hover,
.back-to-top:focus,
.go-to-bottom:hover,
.go-to-bottom:focus {
color: white;
}
.ui-toc-dropdown {
background-color: #333;
}
.ui-toc-label.btn {
background-color: #191919;
color: white;
}
.ui-toc-dropdown .nav>li>a:focus,
.ui-toc-dropdown .nav>li>a:hover {
color: white;
border-left: 1px solid white;
}
.markdown-body blockquote {
color: #bcbcbc;
}
.markdown-body table tr {
background-color: #5f5f5f;
}
.markdown-body table tr:nth-child(2n) {
background-color: #4f4f4f;
}
.markdown-body code,
.markdown-body tt {
color: #eee;
background-color: rgba(230, 230, 230, 0.36);
}
a,
.open-files-container li.selected a {
color: #5EB7E0;
}
</style>
# Introduction à JDBC
Accès aux bases de données en Java
---
# Introduction
- ***JDBC*** : Java Data Base Connectivity
- Framework permettant l'accès aux bases de données relationnelles dans un programme Java
- Indépendament du type de la base utilisée (mySQL, Oracle, Postgres ...)
- Seule la phase de connexion au SGBDR change
- Permet de faire tout type de requêtes
- Sélection de données dans des tables
- Création de tables et insertion d'éléments dans les tables
- Gestion des transactions
- ***Packages*** : java.sql et javax.sql
---
# Principes généraux d'accès à une BDD
- Première étape
- Préciser le type de driver que l'on veut utiliser
- 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 la base utilisée
----
- 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
---
# Connexion au SGBD
- Classe `java.sql.DriverManager`
- Gestion du contrôle et de la connexion au SGBD
- Méthodes principales
- `static void registerDriver(Driver driver)`
- Enregistre le driver (objet driver) pour un type de SGBD particulier
- Le driver est dépendant du SGBD utilisé
----
- Méthodes principales
- `static Connection getConnection(String url, String user, String password)`
- Crée une connexion permettant d'utiliser une base
- url : identification de la base considérée sur le SGBD
- Format de l'URL est dépendant du SGGB utilisé
- user : nom de l'utilisateur qui se connecte à la base
- password : mot de passe de l'utilisateur
---
# Gestion des connexions
- Interface `java.sql.Connection`
- Préparation de l'exécution d'instructions sur la base, 2 types
- Instruction simple : classe `Statement`
- On exécute directement et une fois l'action sur la base
----
- Préparation de l'exécution d'instructions sur la base, 2 types
- Instruction paramétrée : classe `PreparedStatement`
- L'instruction est générique, des champs sont non remplis
- Permet une pré-compilation de l'instruction optimisant les performances
- Pour chaque exécution, on précise les champs manquants
- Pour ces 2 instructions, 2 types d'ordres possibles
- Update : mise à jour du contenu de la base
- Query : consultation (avec un select) des données de la base
---
# Gestion des connexions
- Méthodes principales de `Connection`
- `Statement createStatement()`
- Retourne un état permettant de réaliser une instruction simple
----
- Méthodes principales de `Connection`
- `PreparedStatement prepareStatement(String ordre)`
- Retourne un état permettant de réaliser une instruction paramétrée et pré-compilée pour un *ordre*
- Dans l'ordre, les champs libres (au nombre quelconque) sont précisés par des «?»
- Ex : `select nom from clients where ville=?`
- Lors de l'exécution de l'ordre, on précisera la valeur du champ
- `void close()`
- Ferme la connexion avec le SGBD
---
# Instruction simple
- Classe `Statement`
- `ResultSet executeQuery(String ordre)`
- Exécute un *ordre* de type `SELECT` sur la base.
- Retourne un objet de type *ResultSet* contenant tous les résultats de la requête
- `int executeUpdate(String ordre)`
- Exécute un ordre de type `INSERT, UPDATE, ou DELETE`
- `void close()`
- Ferme l'état
---
# Instruction paramétrée
- Classe `PreparedStatement`
- Avant d'exécuter l'ordre, on remplit les champs avec
- `void set[Type](int index, [Type] val)`
- Remplit le champ en ième position définie par *index* avec la valeur *val* de type *[Type]*
- *[Type]* peut être : String, int, float, long ...
- Ex : `void setString(int index, String val)`
----
- Classe `PreparedStatement`
- `ResultSet executeQuery()`
- Exécute un *ordre* de type SELECT sur la base
- Retourne un objet de type *ResultSet* contenant tous les résultats de la requête
- `int executeUpdate()`
- Exécute un *ordre* de type `INSERT, UPDATE, ou DELETE`
<br>
---
# Lecture des résultats
- Classe `ResultSet`
- Contient les résultats d'une requête `SELECT`
- Plusieurs lignes contenant plusieurs colonnes
- On y accède ligne par ligne puis valeur par valeur dans la ligne
----
- Classe `ResultSet`
- Changements de ligne
- `boolean next()`
- Se place à la ligne suivante s'il y en a une
- Retourne `true` si le déplacement a été fait, `false` s'il n'y avait pas d'autre ligne
- `boolean previous()`
- Se place à la ligne précédente s'il y en a une
- Retourne `true` si le déplacement a été fait, `false` s'il n'y avait pas de ligne précédente
- `boolean absolute(int index)`
- Se place à la ligne numérotée index
- Retourne `true` si le déplacement a été fait, `false` sinon
<br>
---
# Lecture des résultats
- Classe `ResultSet`
- Accès aux colonnes/données dans une ligne
- `[type] get[Type](int col)`
- Retourne le contenu de la colonne col dont l'élément est de type [type] avec [type] pouvant être String, int, float, boolean ...
- Ex : `String getString(int col)`
- Fermeture du ResultSet
- `void close()`
---
# Exception SQLException
- Toutes les méthodes présentées précédemment peuvent lever l'exception `SQLException`
- Exception générique lors d'un problème d'accès à la base lors de la connexion, d'une requête ...
- Plusieurs spécialisations sont définies (voir API)
----
- Toutes les méthodes présentées précédemment peuvent lever l'exception `SQLException`
- Opérations possibles sur cette exception
- `int getErrorCode()` : le code de l'erreur renvoyé par le SGBD (et dépendant du type du SGBD)
- `SQLException getNextException()` : si plusieurs exceptions sont chaînées entre elles, retourne la suivante ou `null` s'il n'y en a pas
- `String getSQLState()` : retourne « l'état SQL » associé à l'exception
---
# Exemple
- Accès à une base MySQL contenant 1 table
- livre (isbn, titre, auteur_nom, auteur_prenom, editeur, annee)
- Paramètres de la base
- Fonctionne sur votre machine `localhost` sur le port 3306
- Base s'appelle « bibliotheque »
- Utilisateur qui se connecte : vous devez les connaître (root/admin/...) et le mot de passe associé.
---
# Exemple
- Création de la connexion à la base
- `Connection conn`;
```java
// chargement du driver MySQL
Class.forName("com.mysql.jdbc.Driver");
// création de la connexion
conn = DriverManager.getConnection('jdbc:mysql://localhost:3306/bibliotheque', 'forma', 'samsam');
//note: la syntaxe du premier argument dépend du type du SGBD
```
---
# Exemple
- Exécution d'une instruction simple de type `SELECT`
- Lister toutes les caractéristiques de tous les livres
```java
Statement req;
ResultSet res;
String isbn;
String titre;
String auteur_nom;
String auteur_prenom;
String editeur;
Int annee;
req = conn.createStatement();
res = req.executeQuery("select * from livre");
```
----
```java
while(res.next()) {
isbn = res.getString(1);
titre = res.getString(2);
auteur_nom = res.getString(3);
auteur_prenom = res.getString(4);
editeur = res.getString(5);
annee = res.getInt(6);
System.out.println(isbn, titre, auteur_nom, auteur_prenom, editeur, annee);
}
req.close();
```
<br>
---
# Exemple
- Exécution d'une instruction simple de type UPDATE
- Ajouter un livre :
```java
Statement req;
int nb;
req = conn.createStatement();
nb = req.executeUpdate("insert into livre values ('978-2070368228','1984', 'george', 'orwell
', 'Gallimard', 1972)");
System.out.println("nombre de lignes modifiées : "+nb);
req.close();
```
---
# Exemple
- Instruction paramétrée de type SELECT
- Retourne tous les livres
```java
PreparedStatement req;
ResultSet res;
String isbn;
String titre;
String auteur_nom;
String auteur_prenom;
String editeur;
Int annee;
req = conn.prepareStatement("select * from livre where isbn = ?");
req.setString(1, "978-2070368228");
res = req.executeQuery();
```
----
```java
while(res.next()) {
isbn = res.getString(1);
titre = res.getString(2);
auteur_nom = res.getString(3);
auteur_prenom = res.getString(4);
editeur = res.getString(5);
annee = res.getInt(6);
System.out.println(isbn, titre, auteur_nom,
auteur_prenom, editeur, annee);
}
req.close();
---
# Exemple
- Instruction paramétrée de type UPDATE
- Ajout de 2 nouveaux livres
```java
PreparedStatement req;
int nb;
req = conn.prepareStatement("insert into livre values (?, ?, ?, ?, ?, ?)");
req.setString(1, "978-2409025273");
req.setString(2, "Flutter - Développez vos applications mobiles multiplateformes avec Dart");
req.setString(3, "Trillard");
req.setString(4, "Julien");
req.setString(5, "Eni Editions");
req.setInt(6, 2020);
```
----
```java
nb = req.executeUpdate();
System.out.println(nb);
req.setString(1, "978-2212678406");
req.setString(2, "Le livre de Java premier langage");
req.setString(3, "Tasso");
req.setString(4, "Anne");
req.setString(5, "Eyrolles");
req.setInt(6, 2019);
nb = req.executeUpdate();
System.out.println(nb);
req.close();
```
---
# Transaction
- Fonctionnalité avancée
- Gestion des transactions
- Transaction
- Un ensemble d'action est effectué en entier ou pas du tout
- Voir documentation spécialisée
---
# Allons-y.
## C'est cool la théorie mais on va se faire un petit projet pour tester tout cela.
---
# Let's go
- Commençons par créer un projet Maven sans archetype.
- Nous le nommerons :
- GroupID : com.m2iformation
- Artifact : startJdbc
- Vous devriez retrouver donc votre pom.xml avec l’architecture de Maven.
- Nous devons rajouter une dépendance afin de pouvoir faire communiquer le Java et le SQL.
```xml
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
</dependencies>
```
----
- Continuons en créant un package dans notre src / main / java, que l’on nommera : com.m2iformation.startJdbc (GroupID + Artifact)
- A l’intérieur, créons une classe Java : StartJdbc, sans oublier la méthode static main. Soit en cochant la case dans eclipse, lors de la création de la classe ou avec le raccourci main(eclipse) / psvm(IntelliJ). Nous n’allons pas gérer pour le moment les Exceptions, donc nous rajouterons throws Exception à notre main.
- Notre projet est prêt. Il nous reste à utiliser notre API afin de faire nos requêtes.
- Nous pourrions appeler notre driver directement dans notre main de cette manière :
```java
package com.m2iformation.jdbcstart;
import com.mysql.jdbc.Driver;
public class StartJdbc {
public static void main(String[] args) throws Exception {
Driver driver = new Driver();
}
}
```
---
# Quel peut-être le problème si l’on fait cela?
---
# 🥳 Et oui!! Vous êtes trop fort!! 🥳
#### Effectivement si nous souhaitons changer de Base de données pour du MariaDB, PostreSQL, etc., nous aurons un problème car il faudra refaire notre code complet. Alors qu’en utilisant java.sql, nous serons assez générique pour changer le driver par la suite.
---
# Création d’un fichier config
- Pour palier au problème précédent, nous allons créer un fichier de conf.properties où nous rentrerons les informations. Il doit être à la racine du projet.
- Les fichiers .properties sont utilisés fréquemment en java. Nous verrons par la suite lors de l’utilisation de framework.
- Dedans nous allons indiquer certaines informations :
```properties
jdbc.driver.class=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/bibliotheque
jdbc.login=root
jdbc.password=
```
<br>
---
# conf.properties
- jdbc.driver.class : Appel du driver du JDBC suivant le SGBD
- jdbc.url : il faut indiquer le driver utilisé puis l’url pour se connecter à la base de données.
- `Ex : //localhost:3306/nom_de_la_BDD`
- jdbc.login : l’utilisateur pour se connecter à la base de données
- jdbc.password : le mot de passe pour se connecter à la BDD
- Il faut absolument ces 4 informations pour se connecter.
---
# Lire le fichier de config
- Pour pouvoir lire le fichier, il faut utiliser l’objet Properties se trouvant dans java.util.
- `Properties props = new Properties();`
- Nous allons utiliser un try un peu différent.
- Depuis Java 1.7, il existe un : *try-with-resources Statement* [voir le doc](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html).
- Pour résumer, il va nous permettre de faciliter les fermetures des objets AutoCloseable. Ex : BufferedReader, FileInputStream, etc.
---
## The try-with-resources statement
- Un try avant Java 1.7 :
```java
try {
BufferedReader br = new BufferedReader(new FileReader(path));
return br.readLine();
} finally {
if (br != null) br.close();
}
```
- Un try après Java 1.7 :
```java
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
```
- Plus besoin de finally pour close()! A la fermeture de l’accolade, l’objet se ferme si il implémente AutoCloseable. Vérifiez sur la bibliothèque de Java si l’objet utilisé implémente cette interface.
---
## Continuons l’appel de conf.properties
- Allons y :
```java
Properties props = new Properties();
try(FileInputStream fis = new FileInputStream("conf.properties")){
props.load(fis);
}
```
- Il faut savoir que Properties, hérite de HashTable<K,V>
- Afin de tester, si nous récupérons bien la valeur d’une clef dans ce fichier, faites un :
`System.out.println(props.getProperty("jdbc.driver.class"));` en dehors de ce try.
- Oui, en faite le fait de faire un props.load, il charge les K,V en mémoire, ce qui nous permet de les récupérer par la suite.
---
# Chargement de la classe du driver
- Après avoir tester, il va falloir charger la classe afin de faire des requêtes.
- Pour charger une classe en Java, nous allons utiliser `Class.forName()`
- Nous voulons charger notre driver, comment feriez-vous?
---
# Alors ?
---
# 🥳 Encore Félicitations 🥳
- Oui, effectivement, il faut faire :
```java
Class.forName(
props.getProperty("jdbc.driver.class")
);
```
- Ceci a permis de l’enregistrer dans DriverManager
<br>
Super, nous avançons doucement mais surement.
---
# Connection
- C’est bien mais il va falloir se connecter!! Arrêtons de tourner autour du pot ! 😂
- Utilisons notre DriverManager :
- `DriverManager.getConnection(url, login, password);`
- Pour éviter les props.getProperty("…") dans les paramètres, nous allons créer des variables pour cela.
```java
String url = props.getProperty("jdbc.url");
String login = props.getProperty("jdbc.login");
String password = props.getProperty("jdbc.password");
Connection connection = DriverManager.getConnection(url, login, password);
```
----
- Attention car une connection doit être fermée. Donc 2 possibilités, un try et un finally ou le try-with-resources.
- Je vous laisse faire le dernier, car nous sommes de bons développeurs.
⚠️ Attention a bien utiliser la bibliothèque java.sql ⚠️
---
# Notre première requête
- Nous allons commencer par une insertion dans notre table livre.<!-- .element: class="fragment" -->
- Ecrivez la requête dans une chaîne de caractères : ⏳<!-- .element: class="fragment" -->
- ⌛️ : temps écoulé.<!-- .element: class="fragment" -->
- `String req = "insert into livre values ('978-2070368228','1984', 'george', 'orwell', 'Gallimard', 1972)"`.<!-- .element: class="fragment" -->
----
- Si l’on souhaite exécuter notre requête, il va falloir utiliser l’interface Statement.
- Comment l’écrire :
```java
try (Statement statement = connection.createStatement() ){
statement.executeUpdate(req);
}
```
Exécuter votre code! Si vous avez des erreurs de : server time zone value 'CEST'… rajouter dans l’url :
`?useUnicode=true&useJDBCCompliantTimezoneShift=true
&useLegacyDatetimeCode=false&serverTimezone=UTC`
<br>
---
## executeUpdate(requête)
<br>
- Cette méthode de java.sql.Connection va mettre à jour la base de données.<br><br>
- On va donc pouvoir utiliser les requêtes :
- INSERT
- UPDATE
- DELETE
---
# executeQuery(requête)
- Cette méthode va nous permettre de faire des SELECT, afin de récupérer certaines informations de notre base.
- Comment cela fonctionne :
- Nous allons pouvoir récupérer des informations en utilisant ResultSet
- `String req = "SELECT * FROM livre";`
- `ResultSet resultSet = statement.executeQuery(req);`
- Nous allons donc récupérer plusieurs informations avec notre resultSet.
---
# Utilisation de ResultSet
- Nous allons avoir besoin de plusieurs méthodes de ResultSet :
- `next()` : qui renvoie un boolean si un objet existe après. Nous allons l’utiliser pour boucler et/ou vérifier si un élément suivant existe. Ex => `while(rs.next()){}`
- `getString()` : qui permet de récupérer la valeur d’une colonne varchar. On peut soit rentré un numéro de colonne ou son nom.
- `getInt()` / `getLong()` / etc …
<br>
⚠️ Contrairement en java, la première colonne commence à 1 et non à 0 ⚠️
---
# Utilisation de ResultSet - suite
- Voici un exemple de code :
```java
String req = "SELECT * FROM livre";
try (Statement statement = connection.createStatement() ){
ResultSet resultSet = statement.executeQuery(req);
while (resultSet.next()){
System.out.println(resultSet.getString("titre"));
}
}
```
----
Nous avons vu dans le cour qu'il y avait mieux.
Donc nous allons préférer utiliser le prepareStatement() au lieu de createStatement().
```java
try(PreparedStatement statement = conn.prepareStatement("select * from livre where isbn = ?")){
statement.setString(1, "978-2070368228");
ResultSet resultSet = statement.executeQuery();
...
}
```
---
# The end
## Amusez vous maintenant.
{"metaMigratedAt":"2023-06-15T20:13:16.665Z","metaMigratedFrom":"YAML","title":"JDBC","breaks":false,"description":"Formation JDBC","robots":"noindex, nofollow","slideOptions":"{\"transition\":\"slide\",\"slideNumber\":true,\"previewLinks\":true,\"overview\":true,\"width\":1366,\"height\":768}","contributors":"[{\"id\":\"35198f6d-8c66-4636-8d98-b19eb0b4c48c\",\"add\":20417,\"del\":441}]"}