定义和查询嵌套关系

有时,您可能需要查询包含三个或更多表格的集合,这些表格之间互相关联。在这种情况下,您需要定义各个表之间的嵌套关系。

在音乐在线影音应用示例中,假设您想要查询所有用户、每个用户的所有播放列表以及每个用户的各个播放列表中包含的所有歌曲。用户与播放列表之间存在一对多关系,而播放列表与歌曲之间存在多对多关系。以下代码示例显示了代表这些实体以及播放列表与歌曲之间多对多关系的交叉引用表的类:

@Entity
data class User(
    @PrimaryKey val userId: Long,
    val name: String,
    val age: Int
)

@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val userCreatorId: 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
)

首先,按照常规方法使用数据类和 @Relation 注释在集合中的两个表格之间建立关系。以下示例展示了一个 PlaylistWithSongs 类,该类可在 Playlist 实体类和 Song 实体类之间建立多对多关系:

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
        parentColumns = ["playlistId"],
        entityColumns = ["songId"],
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)

定义表示此关系的数据类后,请创建另一个数据类,用于在集合中的另一个表与第一个关系类之间建立关系,并将现有关系嵌套到新关系中。以下示例展示了一个 UserWithPlaylistsAndSongs 类,该类可在 User 实体类和 PlaylistWithSongs 关系类之间建立一对多关系:

data class UserWithPlaylistsAndSongs(
    @Embedded val user: User,
    @Relation(
        entity = Playlist::class,
        parentColumns = ["userId"],
        entityColumns = ["userCreatorId"]
    )
    val playlists: List<PlaylistWithSongs>
)

UserWithPlaylistsAndSongs 类间接地在以下三个实体类之间建立了关系:UserPlaylistSong。图 1 展示了该关系。

UserWithPlaylistsAndSongs 在 User 和 PlaylistWithSongs 之间建立了关系,而 PlaylistWithSongs 又在 Playlist 和 Song 之间建立了关系。
图 1. 音乐在线播放应用示例中关系类的示意图。

如果您的集合中还有其他表,则可以创建类在剩余的每个表和之前的关系类之间建立关系。此流程会在您要查询的所有表之间创建一系列嵌套关系。

最后,向数据访问对象 (DAO) 类添加一个函数,用于提供您的应用所需的查询函数。此函数需要 Room 运行多次查询,因此应添加 @Transaction 注释,以确保整个操作以原子方式运行:

@Transaction
@Query("SELECT * FROM User")
suspend fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>