- Ressource: R2.02
- Responsables:
- Besoin d'aide ?
L’objectif de ce document est de vous présenter une méthode d’accès à un SGBD Relationnel à travers le langage de programmation Java
. Pour cela, nous allons dans un premier temps présenter l’API JDBC (Java DataBase Connectivity). C’est un ensemble de classes permettant d’exécuter des ordres SQL de manière générique. En effet, l’API JDBC est construit autour de pilotes (Driver) interchangeables. Un pilote est un module logiciel dédié à une source de données tabulaire (un SGBD-R dans la plupart des cas). Pour utiliser comme source de données MySQL au lieu d’Oracle, il suffit de remplacer le pilote Oracle par celui de MySQL. Ce changement de pilote peut se faire directement par paramétrage sans avoir besoin changer une seule ligne de code ni le recompiler (Il faut tout de même pondérer ces avantages, car dans la pratique, il existe de très nombreuses incompatibilités liées à des implémentations du langage SQL non respectueuses des standards).
L’API JDBC fait partie de Java, mais le pilote propre au SGBD-R n’y est pas. Avant de pouvoir se connecter à une base de données, il faudra donc ajouter à votre projet le fichier jar contenant le pilote adapté. Si vous utilisez un projet Maven, l'ajout se fera par l'insertion d'une nouvelle dépendance dans le fichier pom.xml
de votre projet.
Par exemple pour pouvoir accéder à une base de donnée MySQL, vous devrez rajouter les lignes suivantes entre dans le bloc <dependencies> </dependencies>
:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
L’objectif général de cette partie est de mettre en évidence le schéma de fonctionnement classique de l’API Java d’interaction avec les bases de données relationnelles. Le principe de fonctionnement de cette API est proche de celle d'autres langages objets comme PHP ou C#. D’une manière générale, pour traiter un ordre SQL avec JDBC, il faudra suivre les étapes suivantes :
-
Connexion à la base de données.
-
Création d’une instruction SQL.
-
Exécution de la requête.
-
Traitement de l’ensemble des résultats.
-
Libération des ressources et fermeture de la connexion.
Étant donné que chacune de ses étapes est susceptible de rencontrer des erreurs, il faudra donc rajouter une étape supplémentaire de gestion des exceptions.
Pour illustrer ce propos, nous utiliserons la base de données « Gestion Pédagogique » que vous avez utilisée lors de vos TP de PL/SQL en début d’année. Dans le présent dépôt, vous pourrez trouver un script de génération des tables adapté à MySQL ou Oracle.
En créant votre environnement de développement avec Gitpod, vous aurez accès à un serveur MySQL déjà configuré. Pour pouvoir travailler avec votre propre base de données, vous pouvez essayer de créer une instance d'une base de donnée Postgres chez Elephant SQL. Une fois votre instance créée, vous pourrez récupérer votre URL de connexion dans votre console d'administration.
Le programme Java ci-dessous va être utilisé pour illustrer le fonctionnement de chacune de ces étapes. L’objectif de ce programme est de récupérer la liste des numéros, noms et prénoms de tous les étudiants habitant à Aix-en-Provence pour l’afficher à l’écran.
// Importer les classes jdbc
import java.sql.*;
public class testJDBC {
// Chaîne de connexion
static final String CONNECT_URL = "jdbc:mysql://localhost:3306/gestionPedaBD";
static final String LOGIN = "monUser";
static final String PASSWORD = "monPassword";
// La requête de test
static final String req = "SELECT NUM_ET, NOM_ET, PRENOM_ET " +
"FROM ETUDIANT " +
"WHERE VILLE_ET = 'AIX-EN-PROVENCE'";
public static void main(String[] args) throws SQLException {
// Connexion a la base
System.out.println("Connexion a " + CONNECT_URL);
try (Connection conn = DriverManager.getConnection(CONNECT_URL,LOGIN,PASSWORD)){
System.out.println("Connecte\n");
// Creation d'une instruction SQL
Statement stmt = conn.createStatement();
// Execution de la requête
System.out.println("Execution de la requête : " + req );
ResultSet rset = stmt.executeQuery(req);
// Affichage du résultat
while (rset.next()){
System.out.print(rset.getInt("NUM_ET") + " ");
System.out.print(rset.getString("NOM_ET") + " ");
System.out.println(rset.getString("PRENOM_ET"));
}
// Fermeture de l'instruction (libération des ressources)
stmt.close();
System.out.println("\nOk.\n");
} catch (SQLException e) {
//Ceci n'est pas une gestion réaliste des erreurs
e.printStackTrace();// Arggg!!!
System.out.println(e.getMessage() + "\n");
}
}
}
Les différentes étapes détaillées ci-dessous mentionnent de nombreuses classes contenues dans les paquetages java.sql.*
et javax.sql.*
. Pour connaître les détails sur chacune de ces classes, vous êtes invités à lire la Javadoc que vous trouverez à l’adresse suivante : https://docs.oracle.com/en/java/javase/17/docs/api/index.html.
La première étape qui permet d’interagir avec une base de données est la connexion. Il faut initialiser un objet du type Connection
grâce à la méthode getConnection()
de la classe DriverManager
.
Une fois la connexion établie, il faut créer un objet matérialisant l’ordre SQL à exécuter. Cet objet du type Statement
est obtenu en appelant la méthode createStatement()
de notre connexion. Il existe trois types d’ordre :
-
Les
Statement
: Ils permettent d’exécuter n’importe quelle requête sans paramètre. La requête est interprétée par le SGBD au moment de son exécution. Ce type d’ordre est à utiliser principalement pour les requêtes à usage unique. -
Les
PreparedStatement
: Ils permettent de pré-compiler un ordre avant son exécution. Ils sont particulièrement importants pour les ordres destinés à être exécutés plusieurs fois comme les requêtes paramétrées. -
Les
CallableStatement
: Ils sont destinés à l’appel des procédures stockées.
Afin d’exécuter une requête, il suffit de faire appel à l’une des méthodes executeXXXX()
de l’objet Statement
que l’on vient de créer. Dans l’exemple ci-dessus on utilise la méthode executeQuery()
en lui passant en paramètre une chaîne de caractères (string
) contenant la requête (comme Execute Imediate
de PL/SQL). Cette méthode retourne un objet du type ResultSet
contenant l’ensemble des résultats de la requête. Il faut noter que si l’ordre SQL est une mise à jour des données (Insert
, Update
, Delete
), il faudra alors l’exécuter avec la méthode executeUpdate()
qui retourne un entier correspondant au nombre de lignes impactées par la mise à jour.
La manipulation du résultat d’une requête se fait à travers un objet du type ResultSet
. Le résultat se manipule, comme avec les curseurs de PL/SQL, ligne après ligne. Ainsi, l’objet ResultSet
maintient un pointeur vers la ligne courante. La manipulation de ce pointeur se fait principalement avec la méthode next()
qui permet d’avancer le pointeur sur la ligne suivante. Lors de la création du ResultSet
ce pointeur est positionné sur une ligne spéciale appelée le gap. Cette ligne est située une ligne avant la première ligne du résultat. De ce fait, la première ligne n’est pointée qu’après le premier appel à next()
. Lorsque le pointeur est positionné après la dernière ligne, next()
retourne la valeur false
. Pour parcourir linéairement l’intégralité d’un ResultSet
, on utilise donc une boucle while
avec next()
comme prédicat de continuation. Le corps de la boucle est dédié à la manipulation de la ligne (tuple) couramment pointée.
Afin de récupérer les valeurs des attributs du tuple courant, on utilise l’une des différentes méthodes getXXXX()
(où XXXX
désigne le type de l’attribut que l’on souhaite récupérer). Par exemple, pour récupérer un entier, on utilise getInt()
et pour récupérer un booléen on utilise getBoolean()
. Le paramètre passé à cet accesseur permet de choisir l’attribut à récupérer. Il existe deux façons pour désigner un attribut. La première (celle de l’exemple) consiste à utiliser une string
contenant le nom de la colonne souhaitée. La seconde quant à elle, passe en paramètre un entier (int
) contenant la position (dans le Select
) de l’attribut à récupérer. Attention, contrairement à l’habitude en programmation, les attributs sont numérotés à partir de 1 (et non de 0). Par exemple, si l’on souhaite récupérer la valeur de l’attribut NUM_ET
(le premier dans notre Select
), il faudra faire : rset.getInt(1)
.
Tant que l’on utilise un Statement
ou une Connection
, le système nous alloue un certain nombre de ressources. Maintenir ces ressources disponibles a un coût non négligeable. Ainsi, comme toujours en informatique, pour éviter le gaspillage (et donc un ralentissement inutile) il faut libérer les ressources dès qu’elles ne sont plus nécessaires. Pour ce faire, il suffit d’appeler la méthode close()
des objets Statement
et Connection
.
La grande majorité des classes de JDBC sont susceptibles de lever des exceptions lorsqu’elles rencontrent des erreurs. C’est pour cela qu’il faut toujours encadrer le code JDBC par un bloc try/catch
. Les exceptions levées sont toutes des classes filles de SQLException
.
Cloner le dépôt IUTInfoAix-R202/TutoJdbc
et l'importer dans votre IDE (je vous recommande d'utiliser Gitpod pour vous simplifier la configuration de votre projet). Si vous utilisez un server de base de données externe, le fichier java donné en exemple devra être adapté avec les informations vers votre base de données (nom d'hôte, login, mot de passe).
N'oubliez pas d'exécuter le script de création de table adapté au SGBD que vous utilisez (contenu dans les fichier gestion_peda_XXXXXX.sql
).
Lancer la classe TestJDBC
pour vérifier que tout fonctionne.
Si vous n'êtes pas certain de pouvoir disposer d'un environnement correctement configuré, vous pouvez ouvrir le dépôt dans l'IDE en ligne mis à disposition avec l'outil Gitpod.
Gitpod est un outil permettant de créer des environnements de développement éphémères. Ces environnements permettent aux développeurs de disposer à chaque instant, d'un IDE prêts avec tous les outils et dépendances pré-paramétrés.
Pour ouvrir ce tutoriel avec Gitpod, vous pouvez simplement cliquer sur le bouton suivant et vous laisser guider après avoir cliqué sur le bouton "Continue with Github" :
Pour disposer de Gitpod sur tous vos projets hébergés sur Github, suivez les étapes suivantes :
-
Créez un compte Gitpod en vous rendant sur la page Get started. Identifiez-vous avec votre compte github en cliquant sur le bouton "Continue with Github". Si vous avez déjà obtenu votre pack Github Education, vous pouvez bénéficier de l'offre 100 h/mois. N'oubliez pas d'en faire la demande dans les réglages de votre compte Gitpod.
-
Installez l'application Gitpod. L'application GitHub de Gitpod est similaire à un serveur CI et préparera en permanence des pré-constructions pour toutes vos branches et demandes d'extraction - vous n'avez donc pas à attendre que Maven ou NPM télécharge Internet lorsque vous souhaitez commencer à travailler.
-
Démarrez votre premier espace de travail avec une pré-construction en préfixant l'URL du référentiel par https://gitpod.io#prebuild/. Gitpod affiche l'état d'avancement de la pré-construction en exécutant les commandes
init
du fichier.gitpod.yml
avant de lancer un espace de travail. Plus tard, lorsque vous créez un nouvel espace de travail sur une branche, ou une Pull-Request, l'espace de travail se charge beaucoup plus rapidement, car toutes les dépendances sont déjà téléchargées et le code est compilé. -
Installez de l'extension navigateur Gitpod sur tous les navigateurs basés sur Chromium (tels que Microsoft Edge, Brave, Chrome, ...) ou sur Firefox. L'extension ajoute simplement un bouton Gitpod sur chaque projet et branche sur GitHub, et Bitbucket qui préfixe l'URL avec http://gitpod.io/# afin que vous puissiez facilement ouvrir un nouvel espace de travail à partir de n'importe quel contexte Git.
-
Personnalisez le thème par défaut en ouvrant le panneau de commande (avec le raccourci clavier
Ctrl+Shift+P
) et en sélectionnantPreferences: Color Theme
. Si vous préférez par exemple les couleurs sombres pour reposer vos yeux, le thème Gitpod dark devrait vous convenir. Vous pouvez rajouter de nouveaux thèmes directement en recherchant dans les extensions (avec le raccourci clavierCtrl+Shift+X
).
Vous pouvez maintenant commencer à traiter les environnements de développement comme des ressources automatisées que vous lancez lorsque vous en avez besoin et fermez (et oubliez) lorsque vous avez terminé votre tâche. Les environnements de développement deviennent totalement éphémères. Attention avec votre offre Github éducation, vous ne disposez que de 100 h mensuels, donc il faut penser à fermer vos espaces de travail quand vous avez terminé de vous en servir (dans tous les cas, ils seront fermés automatiquement après 30 minutes d'inactivité).
Vous pouvez commencer à basculer entre les espaces de travail ou ouvrir plusieurs espaces de travail sur le même contexte. Par exemple, vous pouvez en ouvrir quatre (un pour une fonctionnalité en cours de développement, un pour réviser une PR, un pour une issue et un pour une autre PR).