定義及查詢多對多關係

兩個實體之間的「多對多關係」是指父系實體的每個例項分別對應到子實體的零個或更多例項,反之亦然。

在音樂串流應用程式示例中,請考慮使用者定義的播放清單中的歌曲。每個播放清單可包含許多歌曲,而每首歌曲也可能位於不同播放清單中。因此,Playlist 實體和 Song 實體之間是多對多關係。

請按照下列步驟在資料庫中定義及查詢多對多關係:

  1. 定義關係:建立實體和關聯實體 (交叉參照資料表),以代表多對多關係。
  2. 查詢實體:決定要如何查詢相關實體,並建立資料類別來代表預期的輸出內容。

定義關係

如要定義多對多關係,請先為這兩個實體分別建立類別。由於子實體通常不含父系實體的參照,因此多對多關係與其他關係類型不同。請改為建立第三個類別來代表這兩個實體之間的關聯實體或「交叉參照資料表」。交叉參照資料表必須包含主鍵資料欄,該欄代表資料表內多對多關係中的每個實體。在本例中,交叉參照資料表中的每一個資料列都會對應至一對 Playlist 例項和 Song 例項,其中參照的歌曲會包含在參照的播放清單中。

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;
}

查詢實體

下一個步驟視您想要查詢相關實體的方式而定。

  • 如要查詢「播放清單」和每個播放清單的對應「歌曲」清單,請建立新的資料類別,當中包含單一 Playlist 物件以及該播放清單收錄的所有 Song 物件清單。
  • 如要查詢「歌曲」並列出每首歌曲的對應「播放清單」,請建立新的資料類別,當中包含單一 Song 物件以及收錄該歌曲的所有 Playlist 物件清單。

無論是哪一種情況,您都可以在這些類別的 @Relation 註解中使用 associateBy 屬性,建立實體之間的關係模型,以找出建立 Playlist 實體與 Song 實體之間關係的交叉參照實體。

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;
}

最後,在 DAO 類別中新增方法,以顯示應用程式所需的查詢函式。

  • getPlaylistsWithSongs:這個方法會查詢資料庫,並傳回所有產生的 PlaylistWithSongs 物件。
  • getSongsWithPlaylists:這個方法會查詢資料庫,並傳回所有產生的 SongWithPlaylists 物件。

這些方法會分別要求 Room 執行兩項查詢,因此請在這兩種方法中加入 @Transaction 註解,讓整個作業能以不可分割的形式自動執行。

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();