嵌套关系

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

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

Kotlin

@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"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

Java

@Entity
public class User {
    @PrimaryKey public long userId;
    public String name;
    public int age;
}

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    public long userCreatorId;
    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;
}

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

Kotlin

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

Java

public class PlaylistWithSongs {
    @Embedded public Playlist playlist;
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = Junction(PlaylistSongCrossRef.class)
    )
    public List<Song> songs;
}

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

Kotlin

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

Java

public class UserWithPlaylistsAndSongs {
    @Embedded public User user;
    @Relation(
        entity = Playlist.class,
        parentColumn = "userId",
        entityColumn = "userCreatorId"
    )
    public List<PlaylistWithSongs> playlists;
}

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

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

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

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

Kotlin

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

Java

@Transaction
@Query("SELECT * FROM User")
public List<UserWithPlaylistsAndSongs> getUsersWithPlaylistsAndSongs();