兩個實體之間的「多對多關係」是指父系實體的每個例項分別對應到子實體的零個或更多例項,反之亦然。
在音樂串流應用程式範例中,使用者定義的播放清單中有許多歌曲。每個播放清單可包含許多歌曲,而每首歌曲也可能位於不同播放清單中。因此,Playlist 和 Song 實體之間是多對多關係。
請按照下列步驟,在資料庫中定義及查詢多對多關係:
定義關係
如要定義多對多關係,請先為這兩個實體分別建立類別。由於子實體通常不含上層實體的參照,因此多對多關係與其他關係類型不同。請改為建立第三個類別來代表這兩個實體之間的關聯實體或「交叉參照資料表」。交叉參照資料表必須包含主鍵資料欄,該欄代表資料表內多對多關係中的每個實體。在本例中,交叉比對資料表中的每一個資料列都會對應至一對 Playlist 例項和 Song 例項,其中參照的播放清單會包含參照的歌曲。
@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 )
查詢實體
下一個步驟視您想要查詢相關實體的方式而定。
- 如要查詢「播放清單」和每個播放清單的對應「歌曲」清單,請建立新的資料類別,當中包含單一
Playlist物件以及該播放清單收錄的Song物件清單。 - 如要查詢「歌曲」和每首歌曲的對應「播放清單」清單,請建立新的資料類別,當中包含單一
Song物件以及收錄該歌曲的Playlist物件清單。
無論是哪一種情況,您都可以在這些類別的 @Relation 註解中使用 associateBy 屬性,建立實體之間的關係模型,以找出建立 Playlist 實體與 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> )
最後,在資料存取物件 (DAO) 類別中新增函式,以顯示應用程式所需的查詢函式。
getPlaylistsWithSongs- 查詢資料庫,並傳回所有產生的
PlaylistWithSongs物件。 getSongsWithPlaylists- 查詢資料庫,並傳回所有產生的
SongWithPlaylists物件。
每個函式都需要 Room 執行兩項查詢。在兩個函式中加入 @Transaction 註解,確保作業以不可分割的形式執行。
@Transaction @Query("SELECT * FROM Playlist") suspend fun getPlaylistsWithSongs(): List<PlaylistWithSongs> @Transaction @Query("SELECT * FROM Song") suspend fun getSongsWithPlaylists(): List<SongWithPlaylists>
複合鍵
如果使用複合鍵定義關係,請在 @Relation 註解的 parentColumns 和 entityColumns 中指定多個資料欄。
如要在 Junction 中指定資料欄,請在 Junction 註解中一併使用 parentColumns 和 entityColumns。
在以下範例中,Playlist 具有由 playlistId 和 creatorId 組成的複合主鍵。交叉參照表 PlaylistSongCrossRef 也包含這些資料欄,可參照播放清單。
@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> )