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

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

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

Котлин

@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
)

Ява

@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 :

Котлин

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

Ява

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

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

Котлин

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

Ява

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

Класс UserWithPlaylistsAndSongs косвенно моделирует отношения между всеми тремя классами сущностей: User , Playlist и Song . Это показано на рисунке 1.

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

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

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

Котлин

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

Ява

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