場合によっては、互いに関連している 3 つ以上のテーブルをクエリする必要があります。その場合、テーブル間にネストされたリレーションを定義します。
音楽ストリーミング アプリの例で、全ユーザー、各ユーザーの全プレイリスト、各ユーザーの各プレイリストの全曲をクエリするとします。ユーザーにはプレイリストとの 1 対多のリレーションがあり、プレイリストには曲との多対多のリレーションがあります。次のコード例は、これらのエンティティを表すクラスと、プレイリストと曲との多対多のリレーションを表す相互参照テーブルを示しています。
@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 )
まず、通常どおり、セットの中で 2 つのテーブル間のリレーションを、データクラスと @Relation アノテーションを使用してモデル化します。次の例は、Playlist エンティティ クラスと Song エンティティ クラスとの間の多対多のリレーションをモデル化する PlaylistWithSongs クラスを示しています。
data class PlaylistWithSongs( @Embedded val playlist: Playlist, @Relation( parentColumns = ["playlistId"], entityColumns = ["songId"], associateBy = Junction(PlaylistSongCrossRef::class) ) val songs: List<Song> )
このリレーションを表すデータクラスを定義したら、セット内の別のテーブルと最初のリレーション クラスをモデル化する別のデータクラスを作成し、新しいリレーション内に既存のリレーションを「ネスト」します。次の例は、User エンティティ クラスと PlaylistWithSongs リレーション クラスとの間の 1 対多のリレーションをモデル化する UserWithPlaylistsAndSongs クラスを示しています。
data class UserWithPlaylistsAndSongs( @Embedded val user: User, @Relation( entity = Playlist::class, parentColumns = ["userId"], entityColumns = ["userCreatorId"] ) val playlists: List<PlaylistWithSongs> )
UserWithPlaylistsAndSongs クラスは、3 つのエンティティ クラス User、Playlist、Song のリレーションを間接的にモデル化します。このことを図 1 に示します。
セット内に他にもテーブルがある場合は、残りの各テーブル間のリレーションをモデル化するクラスと、以前のリレーション クラスを作成します。このプロセスでは、クエリを実行するすべてのテーブル間にネストされたリレーションのチェーンが作成されます。
最後に、データアクセス オブジェクト(DAO)クラスに関数を追加して、アプリに必要なクエリ関数を公開します。この関数では Room に複数のクエリを実行させる必要があるため、@Transaction アノテーションを追加して、操作全体がアトミックに実行されるようにします。
@Transaction @Query("SELECT * FROM User") suspend fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>