Definire e eseguire query sulle relazioni many-to-many

Una relazione many-to-many tra due entità è una relazione in cui ogni istanza dell'entità principale corrisponde a zero o più istanze dell'entità secondaria e viceversa.

Nell'esempio dell'app di streaming musicale, considera i brani nelle playlist definite dall'utente. Ogni playlist può includere molti brani e ogni brano può appartenere a molte playlist. Esiste quindi una relazione many-to-many tra le entità Playlist e Song.

Per definire ed eseguire query sulle relazioni many-to-many nel database:

  1. Definisci la relazione: stabilisci le entità e l' entità associativa, o tabella di riferimento incrociato, per rappresentare la relazione many-to-many.
  2. Esegui query sulle entità: determina come vuoi eseguire query sulle entità correlate e crea classi di dati per rappresentare l'output previsto.

Definisci la relazione

Per definire una relazione many-to-many, crea innanzitutto una classe per ciascuna delle due entità. Le relazioni many-to-many sono diverse dagli altri tipi di relazioni perché in genere non esiste un riferimento all'entità principale nell'entità secondaria. Crea invece una terza classe per rappresentare un' entità associativa, o tabella di riferimento incrociato, tra le due entità. La tabella di riferimento incrociato deve avere colonne per la chiave primaria di ogni entità nella relazione many-to-many rappresentata nella tabella. In questo esempio, ogni riga della tabella di riferimento incrociato corrisponde a un accoppiamento di un'istanza Playlist e un'istanza Song in cui la playlist a cui viene fatto riferimento include il brano a cui viene fatto riferimento.

@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
)

Esegui query sulle entità

Il passaggio successivo dipende da come vuoi eseguire query su queste entità correlate.

  • Se vuoi eseguire query sulle playlist e su un elenco dei brani corrispondenti per ogni playlist, crea una nuova classe di dati che contenga un singolo oggetto Playlist e un elenco degli oggetti Song inclusi nella playlist.
  • Se vuoi eseguire query sui brani e su un elenco delle playlist corrispondenti per ogni brano, crea una nuova classe di dati che contenga un singolo oggetto Song e un elenco degli oggetti Playlist che includono il brano.

In entrambi i casi, modella la relazione tra le entità utilizzando la associateBy proprietà nell'@Relation annotazione in ciascuna di queste classi per identificare l'entità di riferimento incrociato che fornisce la relazione tra l'entità Playlist e 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>
)

Infine, aggiungi una funzione alla classe dell'oggetto Data Access Object (DAO) per esporre la funzione di query di cui ha bisogno la tua app.

getPlaylistsWithSongs
Esegue query sul database e restituisce tutti gli oggetti PlaylistWithSongs risultanti.
getSongsWithPlaylists
Esegue query sul database e restituisce tutti gli oggetti SongWithPlaylists risultanti.

Ogni funzione richiede a Room di eseguire due query. Aggiungi l'@Transaction annotazione a entrambe le funzioni per assicurarti che l'operazione venga eseguita in modo atomico.

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

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

Chiavi composite

Se definisci la relazione utilizzando chiavi composite, specifica più colonne in parentColumns e entityColumns dell'annotazione @Relation.

Se devi specificare le colonne in Junction, utilizza parentColumns e entityColumns anche nell'annotazione Junction.

Nell'esempio seguente, Playlist ha una chiave primaria composita composta da playlistId e creatorId. La tabella di riferimento incrociato PlaylistSongCrossRef include anche queste colonne per fare riferimento alla 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>
)