Pour que l’ORM puisse mapper (ou associer) le modèle objet avec une base de données SQLite, il faut maintenant annoter ces classes avec les annotations proposées par Room.
Annotez vos classes
L’annotation@Entity
pour définir une table
La première annotation que nous allons utiliser est l’annotation @Entity
. Il s’agit d’un élément essentiel de la bibliothèque Room qui permet de définir des classes comme des entités. Une entité correspond en réalité à une table dans la base de données. Chaque instance de cette entité correspond alors à une ligne dans la table correspondante en base de données.
Cette annotation est portée directement par la classe que l’on souhaite définir en tant qu’entité. Dans le cadre de la fonctionnalité que nous développons pour l’application PETiSoin, deux entités sont à créer :Animal
etNote
.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Contrairement aux autres classes, vous remarquerez que la classeAddress
n’est pas encore annotée. C’est volontaire. L’idée n’est pas de stocker les informations de l’adresse dans une table spécifique de la base de données mais uniquement d’avoir une approche objet dans la description de notre modèle de données. Nous reviendrons sur la classeAddress
un peu plus loin dans ce chapitre.
Est-ce qu’il est possible de donner un nom à une entité ?
Par défaut, la table porte le nom de la classe sur laquelle l’annotation@Entity
est présente. Si vous souhaitez personnaliser le nom de la table, utilisez l’attributtableName
. Vous pouvez par exemple utiliser cet attribut pour que la tableNote
s’appellenote_table
.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
L’annotation @PrimaryKey
pour spécifier la clé primaire de l’entité
Maintenant que les entités sont définies, descendons d’un niveau pour s’intéresser à leurs attributs. Pour chacune de nos entités, nous allons commencer à nous intéresser aux clés primaires, soit les attributs id
des classes Animal
etNote
.
Pour indiquer qu’un attribut est la clé primaire de la table dans votre base de données, il convient d’annoter l’attribut concerné avec@PrimaryKey
.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Si vous enregistrez cinq animaux dans la base de données sans jamais renseigner leur identifiant, ils porteront automatiquement les identifiants `1`, `2`, `3`, `4` et `5`.
Bonne nouvelle, Room supporte l’auto-incrémentation ! Pour activer cette fonctionnalité :
Utilisez l’attribut
autoGenerate
de l’annotation@PrimaryKey
.Donnez lui la valeur
true
. À noter que la fonctionnalité est désactivée par défaut.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
L’annotation@ColumnInfo
pour personnaliser un champ
À l’image d’une entité, qui par défaut donne son nom à la table dans la base de données, le nom des différents champs d’une table est, par défaut, associé aux attributs de la classe. Si vous souhaitez personnaliser le nom du champ, utilisez l’annotation@ColumnInfo
et son attributname
. Cette annotation est tout particulièrement utile si vous activez l’obfuscation sur votre projet puisqu'elle permet alors de garder la main sur les noms des attributs dans la base de données.
Vous pouvez par exemple utiliser cette annotation et cet attribut sur la classeNote
.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
L’annotation @Embedded
pour lier les classes entre elles
Nous avons ensemble défini les entités et personnalisé l’usage de certains attributs. L’implémentation proposée souffre cependant d’un grand manque : l’absence de lien entre les différents objets.
En effet, à ce stade, pour Room, aucun lien n’est réellement fait entre les classesAnimal
,Address
etNote
. Pour y remédier, commençons par créer du lien entre les classesAnimal
etAddress
. Pour l’implémentation du modèle objet de l’application PETiSoin, deux classes ont été créées pour porter les informations d’une adresse postale et d’un animal. Mais si l’on se réfère à notre modèle physique de données, tous les éléments qui composent l’adresse postale doivent se trouver dans la tableAnimal
.
Pour faire ce lien, utilisez alors l’annotation
@Embedded
sur l’attributaddress
de la classeAnimal
. Cela permet de faire comprendre à Room que l’ensemble des attributs portés par la classeAddress
sont bien à stocker dans la tableAnimal
.L'annotation expose un attribut
prefix
qui permet d’ajouter automatiquement un préfixe aux champs de classe encapsulés dans la table de la base de données. Par exemple, vous pourriez indiquer le mot cléaddress_
en tant que valeur de l’attributprefix
de l’annotation@Embedded
.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Retour sur l’annotation@Entity
pour lier les classes entre elles
Si les classesAnimal
etNote
mappent les deux tables de base de données, aucun lien n’est actuellement fait entre elles. Or, lors de la création du MDP, une relation de type 1:n avait été mise en place. Pour créer ce lien et modéliser cette relation , Room doit savoir que l’attributanimalId
de la classeNote
est en réalité une clé étrangère qui fait référence à l’attributid
de la classeAnimal
.
Nous allons revenir sur l’annotation@Entity
. Bien que précédemment uniquement l’attributtableName
ait été utilisé, cette annotation expose bien d’autres attributs dont un attributforeignKeys
qui permet effectivement de renseigner les clés étrangères d’une entité.
Cet attribut accepte comme valeur un tableau deForeignKeys
. Il s’agit également d’une annotation de la bibliothèque Room pour laquelle il est nécessaire de renseigner plusieurs attributs dont :
entity
qui permet de déterminer à quelle entité la clé étrangère fait référence ;parentColumns
qui permet de déterminer la clé primaire à laquelle la clé étrangère fait référence ;childColumns
qui permet de déterminer la clé étrangère dans l’entité courante.
Dans le cadre de l’application PETiSoin, les données à renseigner sont donc les suivantes.
entity | parentColumns | childColumns |
|
|
|
Nous pouvons alors passer à l’implémentation.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Voici une vidéo qui récapitule les principales étapes pour annoter les classes.
Créez un convertisseur de type
SQLite permet de mettre en place une base de données relationnelle. Et c’est exactement ce que nous venons de faire dans Room en définissant des relations entre les entités et donc les tables de la base de données.
Cependant SQLite présente des limitations, notamment sur les types de données qu’il prend en charge. Sur Android, SQLite supporte les types de données suivants :
TEXT
qui permet de stocker des chaînes de caractères ;INTEGER
qui permet de stocker des nombres entiers ;REAL
qui permet de stocker des nombres décimaux ;BLOB
qui permet de stocker des données binaires brutes comme des images, des fichiers audios, etc.
Heureusement pour nous, Room automatise le travail de conversion entre les types de données (primaire pour Java et basiques pour Kotlin) et les types associés en SQLite. Aussi, l’ensemble des types suivants qui ne sont pas inclus dans SQLite sont en réalité stockés en tant queINTEGER
dans la base de données. Voici une liste de types pour manipuler les nombres entiers.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
En plus de ces simples conversions, Room est très intelligent ! Avec un petit coup de pouce, il peut gérer des types plus complexes qui ne sont pas supportés par SQLite.
Pour illustrer cette intelligence, mettons en évidence le manque de contrôle évident dont l’implémentation de l’application PETiSoin actuelle souffre. Comme vous le savez, l’application doit pouvoir stocker uniquement trois types (ou valeurs) d’animaux : des chats, des chiens et des lapins.
Dans l’implémentation actuelle, la nature de l’animal (chats, chiens, lapins) est porté par l’attributtype
de la classeAnimal
qui est une chaîne de caractères. Le fait que cet attribut soit une chaîne de caractères ne permet pas de contrôler les valeurs qu’on lui donne. On pourrait accidentellement y mettre des valeurs incorrectes comme "éléphant" ou "cah"au lieu de "chat". Pour s’assurer que seuls nos trois types d’animaux souhaités soient utilisés, il est possible d’introduire une énumération. Elle permet de définir un ensemble limité et précis de valeurs acceptées. Ici l’énumération pour définir les types d’animaux s’appelleAnimalType
.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Il est alors possible de faire évoluer le code de la classeAnimal
.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Ok, nous avons défini l’énumération, mais comment la stocker maintenant dans notre base de données ?
Bien que le type AnimalType
ne soit pas un type de données supporté par SQLite, Room utilise en interne ce que l’on appelle un convertisseur de type afin de stocker la valeur de l’énumération sous forme de chaîne de caractères, ce qui correspond au typeTEXT
dans la base de données.
Prenons un autre exemple. Dans l’implémentation actuelle des notes, la date de création est proposée sous la forme d’un timestamp ce qui n’est pas toujours évident à manipuler. Les langages de programmation Java et Kotlin proposent une classeDate
qui semble tout à fait adaptée au besoin. Malheureusement, ni Room, ni SQLite ne savent manipuler la classeDate
.
Vous allez donc devoir aider Room en créant votre propre convertisseur de type. Votre objectif est de convertir la date en timestamp.
1. Modifiez le code de la classeNote
pour changer le type de l’attributcreationDate
.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
2. Créez une classe pour définir les convertisseurs de type dont nous avons besoin. En Java elle contiendra deux méthodes statiques.
Une méthode pour convertir la date vers le timestamp.
Une méthode pour convertir le timestamp vers la date.
Ici, la classe se nomme sobrement "Converters".
Si vous utilisez Java : | Si vous utilisez Kotlin, la logique est la même si ce n’est que les méthodes n’ont pas besoin d’être statiques : |
|
|
3. Annotez cette classe avec@TypeConverter
pour expliciter les méthodes en tant que convertisseur de type.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Voici une vidéo qui récapitule les principales étapes pour créer un convertisseur de types.
À vous de jouer !
Contexte
Dans le chapitre précédent, vous avez créé le modèle objet utile à votre projet. Dans le projet, disponible sur GitHub (Java ou Kotlin), vous devez annoter votre modèle objet pour le transformer en entities.
Consignes
Dans le package
com.openclassRooms.Room.data.entity
, vous devez annoter les classesAnimal
etVaccine
pour les transformer en entités.Pour gérer correctement la date, mettez en place un convertisseur de type.
Livrables
Vous pouvez dupliquer le projet GitHub pour modifier le code source du projet et fournir un projet qui compile.
En résumé
L’annotation
@Entity
permet de désigner une classe qui sera stockée dans une base de données et permet également de définir des relations entre plusieurs classes en précisant les contraintes d’intégrité référentielle qui les lient.L’annotation
@PrimaryKey
permet d’indiquer l’attribut d’une entité qui sert de clé primaire pour la table de la base de données associée.L’annotation
@ColumnInfo
permet de personnaliser les informations de la colonne de la base de données pour un champ donné ;L’annotation
@Embedded
permet d’indiquer que les champs d’une autre classe doivent être inclus dans la table de la base de données de l’entité.L’annotation
@TypeConverter
permet de créer des convertisseurs de types pour convertir des types de données personnalisés en types de données pris en charge par SQLite.
Les entités de l’application PETiSoin sont bien annotées. Nous pouvons donc continuer notre exploration de Room pour définir les opérations autour de la base de données de l’application PETiSoin.