Cours [[5.3 - JDBC]] # TP - JDBC pour manipuler une base de données Programmation Orientée Objet avec Java – Coda 1 ère année – 2026 ## Exercice 1 - rpg_character ### 1.1 - Créez un nouveau projet maven Il doit compiler en java version 25. - groupId : celui que vous avez utilisé dans le TP du Maven - artifactId : `tp-jdbc` ### 1.2 - Ajoutez la dépendance du driver JDBC de SQLite - groupId : `org.xerial` - artifactId : `sqlite-jdbc` - version : `3.51.0.0` ### 1.3 - Créer un package Créez un package correspondant au `groupId` de votre projet maven suivi de `.tp.jdbc.test`. Ex: si votre groupId était `foo.bar.baz`, le nom de votre package serait `foo.bar.baz.tp.jdbc.test`. ### 1.4 - Créer un programme principal Dans le package que vous venez de créer. Ajouter une nouvelle classe java nommée `TestJdbc`. Dans ce fichier ajouter le nécessaire pour que votre programme soit exécutable. Faites en sorte que ce programme principal affiche le texte suivant dans la console : ```text Je vais utiliser une base de données. ``` ### 1.5 - Se connecter à une base de données Dans le programme principal, créer **une connexion** à la base de données en lui donnant l'URL suivante : ```text jdbc:sqlite:test.db ``` **Vous devez gérer les exceptions potentielles** > 💡une connexion à une base de données est une resource `AutoCloseable` En cas d'exception du type `SQLException`, affichez un message d'erreur similaire au suivant : `"Erreur lors de la connexion à la base de données :"` suivi du message correspondant à l'exception. **Exécutez le programme** Un fichier `test.db` devrait être apparu à la racine de votre projet. ### 1.6 - Création du modèle de données Dans votre programme, créez un `Statement`. > 💡un Statement est une resource `AutoCloseable` Exécutez la requête suivante à l'aide du statement. ```sql CREATE TABLE IF NOT EXISTS rpg_character ( id integer not null constraint rpg_character_pk primary key autoincrement, name varchar(255) not null, hp integer not null, def integer not null, money integer not null, atk integer, heal integer, job varchar(20) ) ``` En cas d'erreur `SQLException`, le message suivant doit être affiché : `"Erreur lors de création de la table rpg_character : "` suivi du message correspondant à l'exception. ### 1.7 - Ouvrir la base de données dans IntelliJ IntelliJ dispose d'outils intégrés pour afficher et modifier des bases de données. Double-cliquez sur le fichier `test.db` à la racine de votre projet. Si nécessaire, télécharger le Driver SQLite. ![[intellij-db-test-sqlite.png]] Vérifier la connexion en cliquant sur "Test Connection" ![[intellij-db-test-sqlite-verify-connection.png]] Valider en cliquant sur "OK" --- Ouvrir le menu latéral et les outils de base de données Déplier la base de données `table` jusqu'à voir les colonnes de la table `rpg_character` ![[intellij-db-unfold-table.png]] ### 1.8 - voir les données de la table Double-cliquer sur la table `rpg_character`. ![[intellij-db-open-table.png]] Une vue en tableau apparait. ### 1.9 - Ajouter des lignes Dans la vue tableau, ajouter des lignes. Puis "submit". ![[intellij-db-table-insert-row-submit.png]] La table devrait se mettre à jour; ![[intellij-db-table-rows-inserted.png]] ### 1.10 - Voir le code SQL exécuté par IntelliJ IntelliJ exécute du code SQL pour nous. Nous pouvons le voir dans la partie "Services" en dépliant "Database", puis `test`, puis `rpg_character`. ![[intellij-services-database-test-rpg_character.png]] Nous pourrons nous servir de ces requêtes par la suite dans notre programme. ### 1.11 - Utiliser la Query console - Retourner dans le menu de base de données - Jump to query console - Choisir la query console par défaut Une zone de texte apparait sur la gauche. Dans cette zone de texte, saisir la requête suivante : ```sql SELECT * FROM rpg_character; ``` Puis exécuter la requête. Le résultat s'affiche dans le menu "Services" en bas de l'écran. ![[intellij-query-console-select-rpg_character.png]] ### 1.12 - Écrire une énumération Retourner dans la vue Java. Créez un package correspondant au `groupId` de votre projet maven suivi de `.tp.jdbc.rpg.sql` Ex: si votre groupId était `foo.bar.baz`, le nom de votre package serait `foo.bar.baz.tp.jdbc.rpg.sql` Dans ce package, créer une **énumaration** nommée `CharacterJob`. Elle doit contenenir un attribut `public final String name` initialisé dans le constructeur de `CharacterJob`. Les valeurs du type énuméré sont: ```java WARRIOR("Warrior"), PRIEST("Priest"), PALADIN("Paladin"), MAGE("Mage"), THIEF("Thief"), TOURIST("Tourist"), RPG_CHARACTER(null); ``` Ajouter une méthode static dans l'énum pour créer une valeur d'enum à partir de son attribut. ```java CharacterJob job = CharacterJob.fromName("Warrior"); // devrait retourner WARRIOR CharacterJob job = CharacterJob.fromName(null); // devrait retourner RPG_CHARACTER ``` ### 1.13 - Écrire un record Dans un nouveau fichier, créer un **record** nommé `RpgCharacterData`. Celui-ci doit contenir les attributs suivants : - `Integer id` - `String name` - `int hp` - `int def` - `int money` - `CharacterJob job` - `Integer atk` - `Integer heal` ### 1.14 Écrire une requête SQL dans le code Java À partir du statement existant. Exécuter la requête suivante afin qu'elle retourne un `ResultSet`. ```sql SELECT id, name, hp, def, money, job, atk, heal FROM rpg_character ``` Tant que le `ResultSet` a des lignes : Récupérer les valeurs des colonnes : - `id` - `name` - `hp` - `def` - `money` - `job` - `atk` - `heal` Instancier un `RpgCharacter` à partir de ces valeurs. Afficher le contenu du `RpgCharacter` dans la console. Ex. Le résultat devrait ressembler à ceci : ```text [ RpgCharacterData[id=1, name=Rincevent, hp=20, def=0, money=50, atk=1, heal=0, job=MAGE], RpgCharacterData[id=2, name=Ridculle, hp=30, def=1, money=150, atk=3, heal=0, job=MAGE], RpgCharacterData[id=3, name=Cohen, hp=80, def=3, money=120, atk=7, heal=0, job=WARRIOR], RpgCharacterData[id=4, name=Lothar, hp=120, def=3, money=200, atk=4, heal=10, job=PALADIN] ] ``` ## Exercice 2 - CRUD Dans la suite de cet exercice, nous allons - créer des opérations courantes - ajouter - modifier - supprimer - lire ### 2.1 - Renommer `RpgCharacter` Renommer le record `RpgCharacter` en `RpgCharacterData`. ### 2.2 - Créer une interface `RpgCharacters` ```java /** * Permet de lire, écrire, modifier, supprimer depuis une source de données générique. * <p> * Peut être implémenté par une base de données, en mémoire, depuis des fichiers... * <p> * Aucun détails d'implémetation ne devrait être visible dans cette interface */ public interface RpgCharacters { /** * Récupère tous les personnages */ List<RpgCharacterData> all(); /** * Récupère tous les personnages pour un métier donné * @param job métier * @return tous les personnages */ List<RpgCharacterData> allByJob(CharacterJob job); /** * Récupère un personnage par son identifiant * @param id * @return le personnage ou vide */ Optional<RpgCharacterData> find(int id); /** * Supprime un personnage par son identifiant * @param id */ void delete(int id); /** * Supprime un personnage par son identifiant * @param characterData */ void update(RpgCharacterData characterData); /** * Créer un personnage * <p> * <b>Note:</b> l'id du paramètre sera ignoré car il est généré par la couche de persistance. * @param characterData * @return identifiant du personnage créé */ int create(RpgCharacterData characterData); } ``` ### Implémenter l'interface Créer une classe `SqliteRpgCharacters` qui implémente l'interface `RpgCharacters`. Elle s'appuiera sur la base de données pour son implémentation. ### Bonus Écrire un programme **en ligne de commande** qui permette de : - lister le `nom` et `id` tous les personnages disponibles - d'afficher les détails d'un personnage par son `id` - de supprimer un personnage par son `id` - de modifier le `nom` d'un personnage - d'ajouter un personnage ## Exercice 3 - RPG + database - ajouter une dépendance au projet rpg existant - instancier nos personnage depuis leur données provenant de la base de données. ### 3.1 - Configurer le dépot nexus Dans le `pom.xml` Ajouter les lignes suivantes pour configurer le dépôt nexus du cours de java. ```xml <distributionManagement> <repository> <id>coda_lab</id> <url>https://nexus.baldir.fr/repository/coda_lab/</url> <name>coda_lab</name> </repository> </distributionManagement> <repositories> <repository> <id>coda_lab</id> <url>https://nexus.baldir.fr/repository/coda_lab/</url> <name>coda_lab</name> </repository> </repositories> ``` ### 3.2 - Inclure la lib rpg depuis le nexus. Ajouter la dépendance `rpg-lib` il s'agit du corrigé du TP RPG déployé sur un repository maven en tant que bibliothèque. ```xml <dependency> <groupId>school.coda.rpg</groupId> <artifactId>rpg-lib</artifactId> <version>1.2</version> </dependency> ``` Pour référence, le code source est disponible sur https://github.com/coda-school/java-mvn-multi-module ### 3.3 `asRpgCharacter` Écrire du code de "mapping" qui permet d'instancier les bons types de personnages à partir de `RpgCharacterData`. L'instance retournée doit correspondre au `job` du personnage. Ce code sera écrit **DANS** `RpgCharacterData`. #### Point de départ ```java public RpgCharacter asRpgCharacter() { // TODO: à implémenter throw new UnsupportedOperationException("RpgCharacterData::asRpgCharacter() n'est pas encore implémenté"); } ``` #### Exemples d'utilisation Ex. devrait instancier un objet de type `RpgCharacter` ```java var data = new RpgCharacterData("Villageois", 10, 0, 12, CharacterJob.RPG_CHARACTER, null, null); RpgCharacter villageois = data.asRpgCharacter(); var data2 = new RpgCharacterData("Sans métier", 10, 0, 12, null, null, null); RpgCharacter noJob = data2.asRpgCharacter(); ``` Ex. devrait instancier un objet de type `Mage` ```java var data = new RpgCharacterData("Rincevent", 40, 2, 25, CharacterJob.MAGE, 2, null); RpgCharacter rincevent = data.asRpgCharacter(); ``` Ex. devrait instancier un objet de type `Warrior` ```java var data = new RpgCharacterData("Cohen", 120, 5, 50, CharacterJob.WARRIOR, 10, null); RpgCharacter cohen = data.asRpgCharacter(); ``` Ex. devrait instancier un objet de type `Paladin` ```java var data = new RpgCharacterData("Lothar", 40, 3, 75, CharacterJob.PALADIN, 5, 10); RpgCharacter lothar = data.asRpgCharacter(); ``` Ex. devrait instancier un objet de type `Priest` ```java var data = new RpgCharacterData("Elune", 50, 7, 0, CharacterJob.PRIEST, 60, 100); RpgCharacter elune = data.asRpgCharacter(); ``` ### 3.4 `asHealer()` Écrire du code de "mapping" qui permet d'instancier un `Optional<Healer>` à partir de `RpgCharacterData`. L'instance retournée doit correspondre au `job` du personnage. Si le personnage n'est pas un `Healer`, retourne un `Optional` vide. Ce code sera écrit **DANS** `RpgCharacterData`. #### Point de départ ```java public Optional<Healer> asHealer() { // TODO: à implémenter throw new UnsupportedOperationException("RpgCharacterData::asHealer() n'est pas encore implémenté"); } ``` Ex. devrait instancier un objet de type `Optional<Paladin>` ```java var data = new RpgCharacterData("Lothar", 40, 3, 75, CharacterJob.PALADIN, 5, 10); Optional<Healer> optionalHealer = data.asHealer(); // Optional.isPresent() --> true // Paladin ``` Ex. devrait instancier un objet de type `Optional<Priest>` ```java var data = new RpgCharacterData("Elune", 50, 7, 0, CharacterJob.PRIEST, 60, 100); Optional<Healer> optionalHealer = data.asHealer(); // Optional.isPresent() --> true // Priest ``` Ex. devrait instancier un `Optional` vide ```java var data = new RpgCharacterData("Cohen", 120, 5, 50, CharacterJob.WARRIOR, 10, null); Optional<Healer> optionalHealer = data.asHealer(); // Optional.isPresent() --> false ``` ### 3.5 `asAttacker()` Écrire du code de "mapping" qui permet d'instancier un `Optional<Attacker>` à partir de `RpgCharacterData`. L'instance retournée doit correspondre au `job` du personnage. Si le personnage n'est pas un `Attacker`, retourne un `Optional` vide. Ce code sera écrit **DANS** `RpgCharacterData`. ```java public Optional<Attacker> asAttacker() { // TODO: à implémenter throw new UnsupportedOperationException("RpgCharacterData::asAttacker() n'est pas encore implémenté"); } ``` Exemples d'usage Ex. devrait instancier un objet de type `Optional<Warrior>` ```java var data = new RpgCharacterData("Cohen", 120, 5, 50, CharacterJob.WARRIOR, 10, null); Optional<Attacker> optionalAttacker = data.asAttacker(); // Optional.isPresent() --> true // Warrior ``` Ex. devrait instancier un `Optional` vide ```java var data = new RpgCharacterData("Elune", 50, 7, 0, CharacterJob.PRIEST, 60, 100); Optional<Attacker> optionalAttacker = data.asAttacker(); // Optional.isPresent() --> false ```