Définir des données à l'aide d'entités Room

Lorsque vous utilisez la bibliothèque de persistance Room pour stocker les données de votre application, vous devez définir des entités représentant les objets que vous souhaitez stocker. Chaque entité correspond à une table de la base de données Room associée et chaque instance d'une entité représente une ligne de données de la table correspondante.

Cela signifie que vous pouvez utiliser des entités Room pour définir votre schéma de base de données sans avoir à écrire de code SQL.

Anatomie d'une entité

Vous définissez chaque entité Room en tant que classe annotée avec @Entity. Une entité Room comprend des champs pour chaque colonne de la table correspondante dans la base de données, y compris une ou plusieurs colonnes qui constituent la clé primaire.

Le code suivant est un exemple d'entité simple qui définit une table User avec des colonnes pour l'ID, le prénom et le nom :

Kotlin

@Entity
data class User(
    @PrimaryKey val id: Int,

    val firstName: String?,
    val lastName: String?
)

Java

@Entity
public class User {
    @PrimaryKey
    public int id;

    public String firstName;
    public String lastName;
}

Par défaut, Room utilise le nom de la classe comme nom de la table de base de données. Si vous souhaitez que la table porte un autre nom, définissez la propriété tableName de l'annotation @Entity. De même, Room utilise les noms de champs comme noms de colonnes dans la base de données par défaut. Si vous souhaitez qu'une colonne porte un autre nom, ajoutez l'annotation @ColumnInfo dans le champ et définissez la propriété name. L'exemple suivant illustre l'utilisation des noms personnalisés pour une table et ses colonnes :

Kotlin

@Entity(tableName = "users")
data class User (
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

Java

@Entity(tableName = "users")
public class User {
    @PrimaryKey
    public int id;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;
}

Définir une clé primaire

Chaque entité Room doit définir une clé primaire qui identifie de manière unique chaque ligne dans la table de base de données correspondante. La méthode la plus simple consiste à annoter une seule colonne avec @PrimaryKey :

Kotlin

@PrimaryKey val id: Int

Java

@PrimaryKey
public int id;

Définir une clé primaire composite

Si les instances d'une entité doivent être identifiées de manière unique par une combinaison de plusieurs colonnes, vous pouvez définir une clé primaire composite en répertoriant ces colonnes dans la propriété primaryKeys de @Entity :

Kotlin

@Entity(primaryKeys = ["firstName", "lastName"])
data class User(
    val firstName: String?,
    val lastName: String?
)

Java

@Entity(primaryKeys = {"firstName", "lastName"})
public class User {
    public String firstName;
    public String lastName;
}

Champs Ignorer

Par défaut, Room crée une colonne pour chaque champ défini dans l'entité. Si une entité possède des champs que vous ne souhaitez pas conserver, vous pouvez les annoter à l'aide de @Ignore, comme indiqué dans l'extrait de code suivant :

Kotlin

@Entity
data class User(
    @PrimaryKey val id: Int,
    val firstName: String?,
    val lastName: String?,
    @Ignore val picture: Bitmap?
)

Java

@Entity
public class User {
    @PrimaryKey
    public int id;

    public String firstName;
    public String lastName;

    @Ignore
    Bitmap picture;
}

Si une entité hérite des champs d'une entité parente, il est généralement plus facile d'utiliser la propriété ignoredColumns de l'attribut @Entity :

Kotlin

open class User {
    var picture: Bitmap? = null
}

@Entity(ignoredColumns = ["picture"])
data class RemoteUser(
    @PrimaryKey val id: Int,
    val hasVpn: Boolean
) : User()

Java

@Entity(ignoredColumns = "picture")
public class RemoteUser extends User {
    @PrimaryKey
    public int id;

    public boolean hasVpn;
}

Room prend en charge plusieurs types d'annotations qui vous permettent de rechercher plus facilement des détails dans les tables de votre base de données. Utilisez la recherche en texte intégral, sauf si la minSdkVersion de votre application est inférieure à 16.

Prise en charge de la recherche en texte intégral

Si votre application nécessite un accès très rapide aux informations de la base de données via la recherche en texte intégral (FTS), vos entités doivent reposer sur une table virtuelle utilisant le module d'extension SQLite FTS3 ou FTS4. Pour utiliser cette fonctionnalité, disponible dans Room 2.1.0 et versions ultérieures, ajoutez l'annotation @Fts3 ou @Fts4 à une entité donnée, comme indiqué dans l'extrait de code suivant :

Kotlin

// Use `@Fts3` only if your app has strict disk space requirements or if you
// require compatibility with an older SQLite version.
@Fts4
@Entity(tableName = "users")
data class User(
    /* Specifying a primary key for an FTS-table-backed entity is optional, but
       if you include one, it must use this type and column name. */
    @PrimaryKey @ColumnInfo(name = "rowid") val id: Int,
    @ColumnInfo(name = "first_name") val firstName: String?
)

Java

// Use `@Fts3` only if your app has strict disk space requirements or if you
// require compatibility with an older SQLite version.
@Fts4
@Entity(tableName = "users")
public class User {
    // Specifying a primary key for an FTS-table-backed entity is optional, but
    // if you include one, it must use this type and column name.
    @PrimaryKey
    @ColumnInfo(name = "rowid")
    public int id;

    @ColumnInfo(name = "first_name")
    public String firstName;
}

Si une table prend en charge le contenu dans plusieurs langues, utilisez l'option languageId pour spécifier la colonne qui stocke les informations de langue pour chaque ligne :

Kotlin

@Fts4(languageId = "lid")
@Entity(tableName = "users")
data class User(
    // ...
    @ColumnInfo(name = "lid") val languageId: Int
)

Java

@Fts4(languageId = "lid")
@Entity(tableName = "users")
public class User {
    // ...

    @ColumnInfo(name = "lid")
    int languageId;
}

Room offre plusieurs autres options pour définir des entités reposant sur FTS, y compris l'ordre des résultats, les types de générateurs de jetons et les tables gérées en tant que contenu externe. Pour en savoir plus sur ces options, consultez la documentation de référence sur FtsOptions.

Indexer des colonnes spécifiques

Si votre application doit être compatible avec les versions du SDK qui ne prennent pas en charge les entités reposant sur une table FTS3 ou FTS4, vous pouvez toujours indexer certaines colonnes de la base de données pour accélérer vos requêtes. Pour ajouter des index à une entité, ajoutez la propriété indices dans l'annotation @Entity, en indiquant les noms des colonnes que vous souhaitez inclure dans l'index ou l'index composite. L'extrait de code suivant illustre ce processus d'annotation :

Kotlin

@Entity(indices = [Index(value = ["last_name", "address"])])
data class User(
    @PrimaryKey val id: Int,
    val firstName: String?,
    val address: String?,
    @ColumnInfo(name = "last_name") val lastName: String?,
    @Ignore val picture: Bitmap?
)

Java

@Entity(indices = {@Index("name"),
        @Index(value = {"last_name", "address"})})
public class User {
    @PrimaryKey
    public int id;

    public String firstName;
    public String address;

    @ColumnInfo(name = "last_name")
    public String lastName;

    @Ignore
    Bitmap picture;
}

Parfois, certains champs ou groupes de champs d'une base de données doivent être uniques. Vous pouvez appliquer cette propriété d'unicité en définissant la propriété unique d'une annotation @Index sur true. L'exemple de code suivant empêche une table de comporter deux lignes contenant le même ensemble de valeurs pour les colonnes firstName et lastName :

Kotlin

@Entity(indices = [Index(value = ["first_name", "last_name"],
        unique = true)])
data class User(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?,
    @Ignore var picture: Bitmap?
)

Java

@Entity(indices = {@Index(value = {"first_name", "last_name"},
        unique = true)})
public class User {
    @PrimaryKey
    public int id;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;

    @Ignore
    Bitmap picture;
}

Inclure les objets basés sur AutoValue

Dans Room 2.1.0 et versions ultérieures, vous pouvez utiliser des classes de valeurs immuables Java, que vous annotez à l'aide de @AutoValue, en tant qu'entités dans la base de données de votre application. Cette prise en charge est particulièrement utile lorsque deux instances d'une entité sont considérées comme égales, si leurs colonnes contiennent des valeurs identiques.

Lorsque vous utilisez des classes annotées avec @AutoValue en tant qu'entités, vous pouvez annoter les méthodes abstraites de la classe à l'aide de @PrimaryKey, @ColumnInfo, @Embedded et @Relation. Toutefois, lorsque vous utilisez ces annotations, vous devez inclure l'annotation @CopyAnnotations à chaque fois afin que Room puisse interpréter correctement les implémentations générées automatiquement par les méthodes.

L'extrait de code suivant présente un exemple de classe annotée avec @AutoValue que Room reconnaît en tant qu'entité :

User.java

@AutoValue
@Entity
public abstract class User {
    // Supported annotations must include `@CopyAnnotations`.
    @CopyAnnotations
    @PrimaryKey
    public abstract long getId();

    public abstract String getFirstName();
    public abstract String getLastName();

    // Room uses this factory method to create User objects.
    public static User create(long id, String firstName, String lastName) {
        return new AutoValue_User(id, firstName, lastName);
    }
}