Иногда вам может потребоваться запросить набор из трех или более таблиц, связанных друг с другом. В этом случае вы определяете вложенные связи между таблицами.
Предположим, что в примере приложения потоковой передачи музыки вы хотите запросить всех пользователей, все плейлисты для каждого пользователя и все песни в каждом плейлисте для каждого пользователя. Пользователи имеют отношение «один ко многим» со списками воспроизведения, а плейлисты имеют отношение «многие ко многим» с песнями. В следующем примере кода показаны классы, представляющие эти сущности, а также таблица перекрестных ссылок для связи «многие ко многим» между плейлистами и песнями:
Котлин
@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.
Если в вашем наборе есть еще таблицы, создайте класс для моделирования отношений между каждой оставшейся таблицей и класс отношений, который моделирует отношения между всеми предыдущими таблицами. Это создает цепочку вложенных связей между всеми таблицами, которые вы хотите запросить.
Наконец, добавьте метод в класс DAO, чтобы предоставить функцию запроса, необходимую вашему приложению. Этот метод требует, чтобы Room выполнял несколько запросов, поэтому добавьте аннотацию @Transaction
, чтобы вся операция выполнялась атомарно:
Котлин
@Transaction
@Query("SELECT * FROM User")
fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>
Ява
@Transaction
@Query("SELECT * FROM User")
public List<UserWithPlaylistsAndSongs> getUsersWithPlaylistsAndSongs();