Определение и запрос вложенных отношений

Иногда может потребоваться запрос к набору из трех или более таблиц, которые связаны между собой. В этом случае вы определяете вложенные связи между таблицами.

Предположим, в примере с приложением для потоковой передачи музыки вам нужно запросить всех пользователей, все плейлисты для каждого пользователя и все песни в каждом плейлисте для каждого пользователя. Пользователи имеют отношение «один ко многим» с плейлистами, а плейлисты имеют отношение «многие ко многим» с песнями. В следующем примере кода показаны классы, представляющие эти сущности, а также таблица перекрестных ссылок для отношения «многие ко многим» между плейлистами и песнями:

@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 косвенно моделирует взаимосвязи между всеми тремя классами сущностей: User , Playlist и Song . Это показано на рисунке 1.

Объект UserWithPlaylistsAndSongs моделирует взаимосвязь между пользователем и объектом PlaylistWithSongs, который, в свою очередь, моделирует взаимосвязь между плейлистом и песней.
Рисунок 1. Диаграмма классов отношений в примере приложения для потоковой передачи музыки.

Если ваш набор данных содержит несколько таблиц, создайте класс для моделирования связи между каждой оставшейся таблицей и предыдущим классом связей. Этот процесс создаст цепочку вложенных связей между всеми таблицами, к которым вы хотите обращаться с запросами.

Наконец, добавьте функцию в класс объекта доступа к данным (DAO), чтобы предоставить доступ к функции запроса, необходимой вашему приложению. Для выполнения нескольких запросов этой функции требуется Room, поэтому добавьте аннотацию @Transaction , чтобы вся операция выполнялась атомарно:

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