m:n-Beziehungen definieren und abfragen

Eine Mehrfachbeziehung zwischen zwei Entitäten ist eine Beziehung, bei der jeder Instanz der übergeordneten Entität null oder mehr Instanzen der untergeordneten Entität entsprechen und umgekehrt.

Denken Sie im Beispiel für die Musik-Streaming-App an die Titel in den benutzerdefinierten Playlists. Jede Playlist kann viele Titel enthalten und jeder Titel kann Teil vieler verschiedener Playlists sein. Daher besteht zwischen der Entität Playlist und der Entität Song eine m:n-Beziehung.

So definieren und abfragen Sie Beziehungen vom Typ „Mehrfach zu Mehrfach“ in Ihrer Datenbank:

  1. Beziehung definieren: Legen Sie die Entitäten und die assoziative Entität (Verweistabelle) fest, um die m:n-Beziehung darzustellen.
  2. Entitäten abfragen: Legen Sie fest, wie Sie die zugehörigen Entitäten abfragen möchten, und erstellen Sie Datenklassen, um die gewünschte Ausgabe darzustellen.

Beziehung definieren

Wenn Sie eine n:n-Beziehung definieren möchten, 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, die eine assoziative Entität oder eine Verweistabelle zwischen den beiden Entitäten darstellt. Die Referenztabelle muss Spalten für den Primärschlüssel jeder Entität in der in der Tabelle dargestellten Beziehung vom Typ „Mehrfach zueinander“ enthalten. In diesem Beispiel entspricht jede Zeile in der Verweistabelle einer Verknüpfung zwischen einer Playlist-Instanz und einer Song-Instanz, bei der der referenzierte Titel in der referenzierten Playlist enthalten ist.

Kotlin

@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"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

Java

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    public String playlistName;
}

@Entity
public class Song {
    @PrimaryKey public long songId;
    public String songName;
    public String artist;
}

@Entity(primaryKeys = {"playlistId", "songId"})
public class PlaylistSongCrossRef {
    public long playlistId;
    public long songId;
}

Entitäten abfragen

Der nächste Schritt hängt davon ab, wie Sie diese verknüpften Entitäten abfragen möchten.

  • Wenn du Playlists und eine Liste der entsprechenden Songs für jede Playlist abfragen möchtest, erstelle eine neue Datenklasse, die ein einzelnes Playlist-Objekt und eine Liste aller Song-Objekte enthält, die in der Playlist enthalten sind.
  • Wenn du Songs und eine Liste der entsprechenden Playlists abfragen möchtest, erstelle eine neue Datenklasse mit einem einzelnen Song-Objekt und einer Liste aller Playlist-Objekte, in denen der Song enthalten ist.

In beiden Fällen modellieren Sie die Beziehung zwischen den Entitäten mithilfe der Property associateBy in der Annotation @Relation in jeder dieser Klassen, um die Verweis-Entität zu identifizieren, die die Beziehung zwischen der Playlist-Entität und der Song-Entität herstellt.

Kotlin

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

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

Java

public class PlaylistWithSongs {
    @Embedded public Playlist playlist;
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Song> songs;
}

public class SongWithPlaylists {
    @Embedded public Song song;
    @Relation(
         parentColumn = "songId",
         entityColumn = "playlistId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Playlist> playlists;
}

Fügen Sie der DAO-Klasse abschließend eine Methode hinzu, um die Abfragefunktion für Ihre App freizugeben.

  • getPlaylistsWithSongs: Mit dieser Methode wird die Datenbank abgefragt und alle resultierenden PlaylistWithSongs-Objekte zurückgegeben.
  • getSongsWithPlaylists: Mit dieser Methode wird die Datenbank abgefragt und alle resultierenden SongWithPlaylists-Objekte zurückgegeben.

Für diese Methoden muss Room jeweils zwei Abfragen ausführen. Fügen Sie daher beiden Methoden die Annotation @Transaction hinzu, damit die gesamte Operation atomar ausgeführt wird.

Kotlin

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

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

Java

@Transaction
@Query("SELECT * FROM Playlist")
public List<PlaylistWithSongs> getPlaylistsWithSongs();

@Transaction
@Query("SELECT * FROM Song")
public List<SongWithPlaylists> getSongsWithPlaylists();