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:
- Zdefiniuj relację: utwórz encje i encję asocjacyjną lub tabelę odniesień krzyżowych, aby reprezentować relację wiele do wielu.
- 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
Playlisti listę obiektówSongznajdują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
Songi listę obiektówPlaylistzawierają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> )