Definiowanie relacji „wiele do wielu” i wyszukiwanie ich

Relacja wiele do wielu między 2 encjami to relacja, w której każda instancja encji nadrzędnej odpowiada 0 lub większej liczbie instancji encji podrzędnej i odwrotnie.

W przykładzie aplikacji do odtwarzania strumieniowego muzyki rozważmy utwory na playlistach zdefiniowanych przez użytkownika. Każda playlista może zawierać wiele utworów, a każdy utwór może należeć do wielu playlist. Dlatego między encjami Playlist i Song występuje relacja wiele do wielu.

Aby zdefiniować relacje wiele do wielu w bazie danych i wysyłać do nich zapytania:

  1. Zdefiniuj relację: utwórz encje i encję asocjacyjną lub tabelę odniesień krzyżowych, aby reprezentować relację wiele do wielu.
  2. Wysyłaj zapytania do encji: określ, jak chcesz wysyłać zapytania do powiązanych encji, i utwórz klasy danych, które będą reprezentować zamierzone dane wyjściowe.

Definiowanie relacji

Aby zdefiniować relację wiele do wielu, najpierw utwórz klasę dla każdej z 2 encji. Relacje wiele do wielu różnią się od innych typów relacji tym, że w encji podrzędnej zwykle nie ma odniesienia do encji nadrzędnej. Zamiast tego utwórz trzecią klasę, która będzie reprezentować encję asocjacyjną , lub tabelę odniesień krzyżowych, między 2 encjami. Tabela odniesień krzyżowych musi zawierać kolumny klucza podstawowego z każdej encji w relacji wiele do wielu reprezentowanej w tabeli. W tym przykładzie każdy wiersz w tabeli odniesień krzyżowych odpowiada parze instancji Playlist i Song, w której przywoływana playlista zawiera przywoływany utwór.

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

Wysyłanie zapytań do encji

Następny krok zależy od tego, jak chcesz wysyłać zapytania do tych powiązanych encji.

  • Jeśli chcesz wysyłać zapytania do playlist i listy odpowiadających im utworów , utwórz nową klasę danych, która będzie zawierać pojedynczy obiekt Playlist i listę obiektów Song znajdujących się na playliście.
  • Jeśli chcesz wysyłać zapytania do utworów i listy odpowiadających im playlist , utwórz nową klasę danych, która będzie zawierać pojedynczy obiekt Song i listę obiektów Playlist zawierających utwór.

W obu przypadkach modeluj relację między encjami za pomocą właściwości associateBy w adnotacji @Relation w każdej z tych klas, aby wskazać encję odniesień krzyżowych, która zapewnia relację między encją Playlist a encją 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>
)

Na koniec dodaj funkcję do klasy obiektu umożliwiającego dostęp do danych (DAO), aby udostępnić funkcję zapytania potrzebną Twojej aplikacji.

getPlaylistsWithSongs
Wysyła zapytanie do bazy danych i zwraca wszystkie obiekty PlaylistWithSongs.
getSongsWithPlaylists
Wysyła zapytanie do bazy danych i zwraca wszystkie obiekty SongWithPlaylists.

Każda funkcja wymaga, aby biblioteka Room wykonała 2 zapytania. Dodaj @Transaction adnotację do obu funkcji, aby zapewnić, że operacja będzie wykonywana atomowo.

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

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

Klucze złożone

Jeśli definiujesz relację za pomocą kluczy złożonych, w adnotacji @Relation określ wiele kolumn w parentColumns i entityColumns.

Jeśli musisz określić kolumny w Junction, użyj parentColumns i entityColumns również w adnotacji Junction.

W poniższym przykładzie Playlist ma złożony klucz podstawowy składający się z playlistId i creatorId. Tabela odniesień krzyżowych PlaylistSongCrossRef zawiera też te kolumny, aby odwoływać się do playlisty.

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