Définir et interroger des relations plusieurs à plusieurs

Une relation de type plusieurs à plusieurs entre deux entités est une relation dans laquelle chaque instance de l'entité parente correspond à zéro instance ou plus de l'entité enfant, et inversement.

Reprenons à nouveau l'exemple de l'application de streaming musical et penchons-nous sur les titres des playlists définies par l'utilisateur. Chaque playlist peut inclure de nombreux titres, et chacun d'eux peut faire partie de différentes playlists. Par conséquent, il existe une relation de type plusieurs à plusieurs entre les entités Playlist et Song.

Pour définir et interroger des relations de type plusieurs à plusieurs dans votre base de données :

  1. Définissez la relation : établissez les entités et l' entité associative (ou tableau de concordance) pour représenter la relation de type plusieurs à plusieurs.
  2. Interrogez les entités : déterminez comment vous souhaitez interroger les entités associées et créez des classes de données pour représenter la sortie prévue.

Définir la relation

Pour définir une relation de plusieurs à plusieurs, créez d'abord une classe pour chacune de vos deux entités. Les relations de type plusieurs à plusieurs se distinguent des autres types de relations, car il n'existe en général aucune référence à l'entité parente dans l'entité enfant. Créez plutôt une troisième classe pour représenter une entité associative (ou tableau de concordance) entre les deux entités. Le tableau de concordance doit comporter des colonnes pour la clé primaire de chaque entité dans la relation de type plusieurs à plusieurs représentée dans le tableau. Dans cet exemple, chaque ligne du tableau de concordance correspond à l'association d'une instance Playlist et d'une instance Song, où la playlist référencée inclut le titre référencé.

@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val playlistName: String
)

@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)

@Entity(primaryKeys = ["playlistId", "songId"], indices = [Index("playlistId", "songId")])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

Interroger les entités

L'étape suivante dépend de la façon dont vous souhaitez interroger ces entités associées.

  • Si vous souhaitez interroger des playlists et une liste des titres correspondant à chaque playlist, créez une classe de données contenant un seul objet Playlist et une liste des objets Song inclus dans la playlist.
  • Si vous souhaitez interroger des titres et une liste des playlists correspondant à chaque titre, créez une classe de données contenant un seul objet Song et une liste des objets Playlist qui incluent le titre.

Dans les deux cas, représentez la relation entre les entités à l'aide de la associateBy propriété de l'@Relation annotation dans chacune de ces classes pour identifier l'entité de concordance déterminant la relation entre l'entité Playlist et l'entité Song.

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
        parentColumns = ["playlistId"],
        entityColumns = ["songId"],
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)

data class SongWithPlaylists(
    @Embedded val song: Song,
    @Relation(
        parentColumns = ["songId"],
        entityColumns = ["playlistId"],
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val playlists: List<Playlist>
)

Enfin, ajoutez une fonction à la classe d'objet d'accès aux données (DAO) pour afficher la fonction de requête nécessaire à votre application.

getPlaylistsWithSongs
Interroge la base de données et renvoie tous les objets PlaylistWithSongs obtenus.
getSongsWithPlaylists
Interroge la base de données et renvoie tous les objets SongWithPlaylists obtenus.

Chaque fonction nécessite que Room exécute deux requêtes. Ajoutez l'@Transaction annotation aux deux fonctions pour vous assurer que l'opération s'exécute de manière atomique.

@Transaction
@Query("SELECT * FROM Playlist")
suspend fun getPlaylistsWithSongs(): List<PlaylistWithSongs>

@Transaction
@Query("SELECT * FROM Song")
suspend fun getSongsWithPlaylists(): List<SongWithPlaylists>

Clés composites

Si vous définissez la relation à l'aide de clés composites, spécifiez plusieurs colonnes dans parentColumns et entityColumns de l'annotation @Relation.

Si vous devez spécifier des colonnes dans Junction, utilisez également parentColumns et entityColumns dans l'annotation Junction.

Dans l'exemple suivant, Playlist possède une clé primaire composite composée de playlistId et creatorId. Le tableau de concordance PlaylistSongCrossRef inclut également ces colonnes pour référencer la playlist.

@Entity(primaryKeys = ["playlistId", "creatorId"])
data class Playlist(
    val playlistId: Long,
    val creatorId: Long,
    val playlistName: String
)

@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)

@Entity(primaryKeys = ["playlistId", "creatorId", "songId"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val creatorId: Long,
    val songId: Long
)

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
        parentColumns = ["playlistId", "creatorId"],
        entityColumns = ["songId"],
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)