两个实体之间的 多对多关系是指这样一种关系:父实体的每个实例对应于子实体的零个或多个实例,反之亦然。
在音乐在线影音应用示例中,考虑用户定义的播放列表中的歌曲。每个播放列表都可以包含多首歌曲,每首歌曲都可以包含在多个播放列表中。因此,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> )