Bei einer m:n-Beziehung zwischen zwei Entitäten entspricht jede Instanz der übergeordneten Entität null oder mehr Instanzen der untergeordneten Entität und umgekehrt.
Betrachten wir als Beispiel die Songs in benutzerdefinierten Playlists in einer Musikstreaming-App. Jede Playlist kann viele Songs enthalten und jeder Song kann zu vielen Playlists gehören. Daher besteht eine m:n-Beziehung zwischen den Entitäten Playlist und Song.
So definieren und fragen Sie m:n-Beziehungen in Ihrer Datenbank ab:
- Beziehung definieren: Legen Sie die Entitäten und die assoziative Entität oder die Querverweistabelle fest, um die m:n Beziehung darzustellen.
- Entitäten abfragen: Legen Sie fest, wie Sie die verknüpften Entitäten abfragen möchten, und erstellen Sie Datenklassen, um die gewünschte Ausgabe darzustellen.
Beziehung definieren
Um eine m:n-Beziehung zu definieren, erstellen Sie zuerst eine Klasse für jede Ihrer beiden Entitäten. M:n-Beziehungen unterscheiden sich von anderen Beziehungstypen, da es in der untergeordneten Entität in der Regel keinen Verweis auf die übergeordnete Entität gibt. Erstellen Sie stattdessen eine dritte Klasse, um eine
assoziative Entität oder Querverweistabelle zwischen den
beiden Entitäten darzustellen. Die Querverweistabelle muss Spalten für den Primärschlüssel aus jeder Entität in der m:n-Beziehung haben, die in der Tabelle dargestellt wird. In diesem Beispiel entspricht jede Zeile in der Querverweistabelle einer Paarung einer Playlist-Instanz und einer Song-Instanz, wobei die referenzierte Playlist den referenzierten Song enthält.
@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 )
Entitäten abfragen
Der nächste Schritt hängt davon ab, wie Sie diese verknüpften Entitäten abfragen möchten.
- Wenn Sie Playlists und eine Liste der entsprechenden Songs für jede Playlist abfragen möchten, erstellen Sie eine neue Datenklasse, die ein einzelnes
Playlist-Objekt und eine Liste derSong-Objekte enthält, die in der Playlist enthalten sind. - Wenn Sie Songs und eine Liste der entsprechenden Playlists für jeden Song abfragen möchten, erstellen Sie eine neue Datenklasse, die ein einzelnes
Song-Objekt und eine Liste derPlaylist-Objekte enthält, die den Song enthalten.
Modellieren Sie in beiden Fällen die Beziehung zwischen den Entitäten mit der
associateBy Property in der @Relation Annotation in jeder dieser
Klassen, um die Querverweisentität zu identifizieren, die die Beziehung
zwischen der Playlist Entität und der Song Entität bereitstellt.
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> )
Fügen Sie schließlich der Klasse des Datenzugriffsobjekts (Data Access Object, DAO) eine Funktion hinzu, um die Abfragefunktion verfügbar zu machen, die Ihre App benötigt.
getPlaylistsWithSongs- Fragt die Datenbank ab und gibt alle resultierenden
PlaylistWithSongs-Objekte zurück. getSongsWithPlaylists- Fragt die Datenbank ab und gibt alle resultierenden
SongWithPlaylists-Objekte zurück.
Für jede Funktion muss Room zwei Abfragen ausführen. Fügen Sie beiden Funktionen die @Transaction
Annotation hinzu, um sicherzustellen, dass der Vorgang atomar ausgeführt wird.
@Transaction @Query("SELECT * FROM Playlist") suspend fun getPlaylistsWithSongs(): List<PlaylistWithSongs> @Transaction @Query("SELECT * FROM Song") suspend fun getSongsWithPlaylists(): List<SongWithPlaylists>
Zusammengesetzte Schlüssel
Wenn Sie die Beziehung mit zusammengesetzten Schlüsseln definieren, geben Sie in parentColumns und entityColumns der Annotation @Relation mehrere Spalten an.
Wenn Sie Spalten in der Junction angeben müssen, verwenden Sie auch parentColumns und entityColumns in der Annotation Junction.
Im folgenden Beispiel hat Playlist einen zusammengesetzten Primärschlüssel, der aus playlistId und creatorId besteht. Die Querverweistabelle PlaylistSongCrossRef enthält auch diese Spalten, um auf die Playlist zu verweisen.
@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> )