• 12 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 26/09/2024

Organisez l’accès aux données avec les DAO

Après avoir structuré vos données avec les entités pour représenter les aspects clés de PETiSoin, l'étape suivante est de faciliter l'accès et la gestion de ces données.

Analysez votre besoin

Manipuler les données d’une base de données nécessite un prérequis : connaître l’ensemble des opérations qu’il est possible de faire sur celle-ci.

Prenez connaissance des opérations CRUD

Au cœur de Room, et des bases de données de manière plus générale, se trouve le concept de CRUD (Create, Read, Update et Delete).

Image représentant les opérations CRUD : Create, Read, Update, Delete avec des traductions en français : Créer, Lire, Mettre à jour et Supprimer.
L’acronyme CRUD

Nom de l’opération

Objectif de l’opération

Nature de l’opération

Create

Ajouter des nouvelles données à la base de données

Écriture

Read

Récupérer des données de la base de données

Lecture

Update

Mettre à jour (MAJ) les données existantes dans la base de données

Écriture

Delete

Supprimer des données de la base de données

Écriture

Définissez le besoin

Rapprochons ces quatre opérations fondamentales du besoin fonctionnel de l’application PETiSoin, à savoir la gestion des animaux et des notes associées.

L’ensemble des quatre opérations doit être implémentée pour les deux entités représentées par les classes AnimaletNotes. Vous aurez donc besoin des opérations suivantes.

  • Création d’un animal.

  • Lecture des informations d’un animal.

  • Mise à jour des informations d’un animal.

  • Suppression de l’ensemble des informations d’un animal.

  • Récupération des informations de l’ensemble des animaux.

  • Création d’une note associée à un animal.

  • Lecture d’une note associée à un animal.

  • Mise à jour d’une note associée à un animal.

  • Suppression d’une note associée à un animal.

  • Récupération de l’ensemble des notes associées à un animal.

Définissez vos interfaces

Maintenant que les différentes opérations de l’application PETiSoin sont connues, il convient de les traduire sous la forme de code Java ou Kotlin et plus particulièrement sous la forme de DAO (Data Access Object ou objets pour accéder aux données).

Euh… C’est quoi un DAO ?

Le DAO est un patron de conception logiciel dont le but est de simplifier l'accès aux données d'une base de données. Il agit comme une interface qui facilite l’interaction entre l’application et la base de données en abstrayant les détails techniques de la connexion, de l'exécution de requêtes et de la gestion des résultats. Votre code sera ainsi plus propre, plus maintenable et plus flexible aux évolutions futures.

L'image représente l'architecture d'une application utilisant des Data Access Objects pour communiquer entre une base de données et l'application PETISoin. Les flèches montrent le flux bidirectionnel de données entre les 3 composants
Le fonctionnement du Data Access Objects

Il est généralement de bonne pratique d’écrire un DAO par table de la base de données. Dans le cadre de la gestion des animaux et des notes, l’application PETiSoin interagit avec deux tables :AnimaletNote. Deux DAO seront donc nécessaires.

Débutons par le DAO lié à la tableAnimal. L'implémentation technique d'un DAO, en Java ou Kotlin, consiste à définir une interface qui spécifie les méthodes pour accéder aux données. Vous devez donc créer une interfaceAnimalDao.

Si vous utilisez Java :

Si vous utilisez Kotlin :

public interface AnimalDao
{
}
interface AnimalDao
{
}

Déclarez la méthode abstraite permettant de récupérer un animal

Pour chacune des opérations liées à la tableAnimal, il est nécessaire de créer une méthode dans l’interface en réfléchissant à sa signature avec :

  1. Le type de données retourné par la fonction ;

  2. Les arguments nécessaires au bon fonctionnement de la fonction ;

  3. Un nom explicite.

Créons ensemble la signature de la méthode qui permet de lire les informations d’un animal.

  1. Le type de retour est ici évident : une instance de la classeAnimal

  2. Pour récupérer les informations d’un animal, la fonction doit savoir de quel animal elle doit récupérer les informations. Elle a donc besoin de pouvoir identifier de manière unique l’animal concerné par l’opération. Pour ça, rien de mieux que l’identifiant de l’animal qui est la clé primaire de la tableAnimalet qui par définition est donc unique.

  3. Finalement, pour le nom de la fonction, on utilise souvent la convention de nommage suivante :{operation}{Type de retour}By{Argument}. 

Pour le nom de l’opération, on utilise généralement les conventions de nommage suivantes :

  • get pour accéder à une ressource ;

  • delete pour supprimer une ressource ;

  • add pour ajouter une ressource ;

  • update pour mettre à jour une ressource.

Dans le cadre de notre exemple, la méthode s’appellera donc :getAnimalById. Il est alors possible de compléter votre interface.

Si vous utilisez Java :                                                                                                                                                          

Si vous utilisez Kotlin :

L'utilisation du mot-clésuspend lors de la déclaration de la méthode est fortement recommandée pour permettre une exécution non bloquante via l’utilisation des coroutines.

public interface AnimalDao
{
  Animal getAnimalById(int id);
}
interface AnimalDao
{
  suspend fun getAnimalById(id: Int): Animal
}

Déclarez la méthode abstraite permettant de récupérer l'ensemble des animaux

Avant de vous donner la solution pour les opérations suivantes, faisons le même exercice pour l’opération dont l’objectif est de récupérer l’ensemble des animaux de la base de données.

  1. Le type de retour sera une collection

  2. Pour récupérer l’ensemble des animaux, la fonction associée n’a pas besoin d’argument car cette méthode doit retourner l’ensemble des animaux. 

  3. Si l’on adapte la convention de nommage décrit plus haut, on obtient comme nom :  getAllAnimals.

Il est ainsi possible de compléter l’interfaceAnimalDao.

Si vous utilisez Java :

Si vous utilisez Kotlin :

public interface AnimalDao
{
  Animal getAnimalById(int id);
  List<Animal> getAllAnimals();
}
interface AnimalDao
{
  suspend fun getAnimalById(id: Int): Animal
  suspend fun getAllAnimals(): List<Animal>
}

Émettez des données en continu à partir de la base de données

Avant de passer à l’implémentation Java et Kotlin des autres opérations, attardons-nous sur la fonction getAllAnimalsque nous venons d’écrire. Imaginons que cette fonction est consommée dans l’écran principal de l’application. Au démarrage de l’application, l’ensemble des animaux présents dans la base de données s’affiche à l’écran. Imaginons maintenant que l’utilisateur ouvre un second écran pour ajouter un nouvel animal puis revienne sur l’écran principal : que se passera-t-il ?

La réponse dépend bien évidemment de ce qui a été implémenté. Mais en général le chargement des données se fait dans la méthode onCreatede l’activité. Aussi, en retournant à l’écran principal, rien ne se passe. La liste des animaux reste la même. Le nouvel animal, fraîchement ajouté dans la base de données, n'apparaît pas. Ceci est dû au fait que la fonctiongetAllAnimalsne renvoie le résultat qu’une seule fois, même si les données sous-jacentes (ajout d’un animal) sont modifiées.

Il est bien évidemment possible de trouver des solutions plus ou moins complexes comme rafraîchir les données au passage dans la méthode onResumeou renvoyer un résultat entre les deux écrans en jouant avec lesIntent. Mais si je vous disais que Room a tout prévu ?

En effet, pour résoudre ce problème, Room supporte une fonctionnalité qui permet aux DAO d’émettre des données en continu à partir de la base de données. Aussi, si un élément (dans notre cas un animal) est inséré, modifié ou supprimé, la fonction renverra automatiquement un nouveau résultat.

Pour bénéficier de cette fonctionnalité, il convient de changer le type de retour de la fonction. Plutôt que de renvoyer une collection, vous pouvez demander à votre fonction de renvoyer :

  • UneLiveDatasi vous utilisez le langage de programmation Java ;

  • UnFlowsi vous utilisez le langage de programmation Kotlin.

Si vous utilisez Java :

En appliquant le type de retourLiveDataà la méthodegetAllAnimals, voici l’interface  AnimalDao

Si vous utilisez Kotlin :

En appliquant le type de retour Flow à la méthodegetAllAnimals, voici l’interface  AnimalDao.         

public interface AnimalDao
{
  Animal getAnimalById(int id);
  LiveData<List<Animal>> getAllAnimals();
}
interface AnimalDao
{
  suspend fun getAnimalById(id: Int): Animal
  fun getAllAnimals(): Flow<List<Animal>>
}

Déclarez la méthode abstraite permettant de récupérer le reste des opérations

Maintenant que l’exercice est compris, vous pouvez terminer l’écriture du DAO AnimalDaoavec les opérations manquantes et écrire le DAO NoteDao.

Si vous utilisez Java : 

Si vous utilisez Kotlin :

public interface AnimalDao
{
  void insertAnimal(Animal animal);
  Animal getAnimalById(int id);
  void updateAnimal(Animal animal);
  void deleteAnimal(Animal animal);
  LiveData<List<Animal>> getAllAnimals();
}
public interface NoteDao
{
  void insertNote(Note note);
  Note getNoteById(int id);
  void updateNote(Note note);
  void deleteNote(Note note);
  LiveData<List<Note>> getAllNotesByAnimalId(int id);
}
interface AnimalDao
{
  suspend fun insertAnimal(animal: Animal)
  suspend fun getAnimalById(id: Int): Animal
  suspend fun updateAnimal(animal: Animal)
  suspend fun deleteAnimal(animal: Animal)
  fun getAllAnimals(): Flow<List<Animal>>
}
interface NoteDao
{
  suspend fun insertNote(note: Note)
  suspend fun getNoteById(id: Int): Note
  suspend fun updateNote(note: Note)
  suspend fun deleteNote(note: Note)
  fun getAllNotesByAnimalId(id: Int): Flow<List<Note>>
}

À vous de jouer !

Contexte

Vous devez continuer l’implémentation technique de la rubrique “Santé” de l’application PETiSoin, en définissant des interfaces DAOs.

Dans le projet, disponible sur GitHub (Java ou Kotlin), une première interface DAOAnimalDaoa déjà été créée mais ce n’est pas suffisant pour permettre le bon fonctionnement de la rubrique “Santé” de l’application.

Consignes

Dans le packagecom.openclassRooms.Room.data.dao , vous devez ajouter l’interface DAO  VaccineDaoavec les méthodes qui permettent les opérations suivantes dans la base de données :

  • Lister les vaccins d’un animal ;

  • Ajouter un vaccin ;

  • Modifier un vaccin ;

  • Supprimer un vaccin.

Livrables

Vous pouvez dupliquer le projet GitHub pour modifier le code source du projet et fournir un projet qui compile.

En résumé

  • Quand on manipule une base de données, il existe 4 opérations : CRUD (Create, Read, Update, Delete).

  • Un DAO est une interface contenant l'ensemble des méthodes décrivant les opérations possibles sur la base de données.

  • Pour émettre des données en continu, il est possible de retourner un type LiveDataen Java et un typeFlowen kotlin.

Vous avez défini les types d’opérations que vous avez besoin d’inclure dans votre base de données. Vous avez aussi découvert le patron de conception logiciel DAO permettant d’exposer l’ensemble des interactions possibles avec votre base de données. La prochaine étape est de rendre fonctionnels ces DAO grâce aux différentes annotations de Room.

Exemple de certificat de réussite
Exemple de certificat de réussite