Choisir des types de relations entre des objets

SQLite étant une base de données relationnelle, vous pouvez définir des relations entre les entités. Cependant, alors que la plupart des bibliothèques de mappage relationnel d'objets permettent aux entités de se référencer entre elles, Room interdit explicitement cette pratique. Pour en savoir plus sur le raisonnement technique qui justifie cette décision, consultez Comprendre pourquoi Room n'autorise pas les références d'objets.

Types de relations

Room est compatible avec les types de relations suivants:

  • Un à un: représente une relation dans laquelle une entité est associée à une autre entité.
  • Un à plusieurs: représente une relation dans laquelle une entité unique peut être associée à plusieurs entités d'un autre type.
  • Plusieurs à plusieurs: représente une relation dans laquelle plusieurs entités d'un type peuvent être associées à plusieurs entités d'un autre type. Cela nécessite généralement une table de jointure.
  • Relations imbriquées (à l'aide d'objets intégrés): représente une relation dans laquelle une entité contient une autre entité en tant que champ, et cette entité imbriquée peut contenir d'autres entités. Cette opération utilise l'annotation @Embedded.

Choisir entre deux approches

Dans Room, il existe deux façons de définir et d'interroger une relation entre les entités. Vous pouvez utiliser:

  • Une classe de données intermédiaire avec des objets encapsulés, ou
  • Méthode de requête relationnelle avec un type renvoyé en multimap.

Si vous n'avez aucune raison spécifique d'utiliser des classes de données intermédiaires, nous vous recommandons d'utiliser le type renvoyé en multimap. Pour en savoir plus sur cette approche, consultez Renvoyer un multimap.

L'approche basée sur des classes de données intermédiaires vous permet d'éviter d'écrire des requêtes SQL complexes, mais elle peut également augmenter la complexité du code, car elle nécessite des classes de données supplémentaires. Pour résumer, l'approche liée à un type renvoyé en multimap requiert davantage d'opérations de vos requêtes SQL, tandis que l'approche de classe de données intermédiaire nécessite davantage de travail dans votre code.

Utiliser l'approche de la classe de données intermédiaire

Dans l'approche de la classe de données intermédiaire, vous définissez une classe de données qui représente la relation entre vos entités Room. Cette classe de données contient les associations entre les instances d'une entité et les instances d'une autre entité en tant qu'objets intégrés. Vos méthodes de requête peuvent ensuite renvoyer des instances de cette classe de données à utiliser dans votre application.

Par exemple, vous pouvez définir une classe de données UserBook pour représenter les utilisateurs de la bibliothèque avec des livres spécifiques récupérés, et définir une méthode de requête pour récupérer une liste d'instances UserBook à partir de la base de données :

Kotlin

@Dao
interface UserBookDao {
    @Query(
        "SELECT user.name AS userName, book.name AS bookName " +
        "FROM user, book " +
        "WHERE user.id = book.user_id"
    )
    fun loadUserAndBookNames(): LiveData<List<UserBook>>
}

data class UserBook(val userName: String?, val bookName: String?)

Java

@Dao
public interface UserBookDao {
   @Query("SELECT user.name AS userName, book.name AS bookName " +
          "FROM user, book " +
          "WHERE user.id = book.user_id")
   public LiveData<List<UserBook>> loadUserAndBookNames();
}

public class UserBook {
    public String userName;
    public String bookName;
}

Utiliser l'approche des types de retour multimap

Avec l'approche de type renvoyé en multimap, vous n'avez pas besoin de définir d'autres classes de données. Spécifiez plutôt un type renvoyé en multimap pour la méthode basée sur la structure de carte que vous souhaitez, ainsi qu'une relation entre vos entités, directement dans votre requête SQL.

Par exemple, la méthode de requête suivante renvoie un mapping de User et de Book pour représenter les utilisateurs de la bibliothèque avec des livres spécifiques récupérés :

Kotlin

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id"
)
fun loadUserAndBookNames(): Map<User, List<Book>>

Java

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id"
)
public Map<User, List<Book>> loadUserAndBookNames();

Créer des objets intégrés

Parfois, vous souhaitez représenter une entité ou un objet de données comme un ensemble cohérent dans votre logique de base de données, même si l'objet contient plusieurs champs. Dans ce genre de situations, vous pouvez utiliser l'annotation @Embedded pour représenter un objet que vous souhaitez décomposer en ses sous-champs dans un tableau. Vous pouvez ensuite interroger les champs intégrés comme vous le feriez pour d'autres colonnes individuelles.

Par exemple, votre classe User peut inclure un champ de type Address, qui représente une composition de champs nommés street, city, state et postCode. Pour stocker les colonnes composées séparément dans le tableau, incluez un champ Address. Cela devrait apparaître dans la classe User annotée avec @Embedded. L'extrait de code suivant le démontre:

Kotlin

data class Address(
    val street: String?,
    val state: String?,
    val city: String?,
    @ColumnInfo(name = "post_code") val postCode: Int
)

@Entity
data class User(
    @PrimaryKey val id: Int,
    val firstName: String?,
    @Embedded val address: Address?
)

Java

public class Address {
    public String street;
    public String state;
    public String city;

    @ColumnInfo(name = "post_code") public int postCode;
}

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

    public String firstName;

    @Embedded public Address address;
}

Le tableau représentant un objet User contient des colonnes portant les noms suivants : id, firstName, street, state, city et post_code.

Si une entité comporte plusieurs champs intégrés du même type, vous pouvez garder chaque colonne unique en définissant la propriété prefix. Room ajoute ensuite la valeur fournie au début de chaque nom de colonne dans l'objet intégré.

Ressources supplémentaires

Pour en savoir plus sur la définition de relations entre des entités dans Room, consultez les ressources supplémentaires ci-dessous.

Vidéos

Blogs