Связь «многие ко многим» между двумя сущностями — это связь, при которой каждому экземпляру родительской сущности соответствует ноль или более экземпляров дочерней сущности, и обратное также верно.
В примере с приложением для потоковой передачи музыки рассмотрим песни в пользовательских плейлистах. Каждый плейлист может включать множество песен, и каждая песня может принадлежать многим плейлистам. Следовательно, между сущностями Playlist и Song существует связь «многие ко многим».
Выполните следующие шаги, чтобы определить и запросить связи «многие ко многим» в вашей базе данных:
- Определите взаимосвязь : создайте сущности и связанную с ними сущность или таблицу перекрестных ссылок, чтобы представить связь «многие ко многим».
- Запрос к сущностям : Определите, как вы хотите запрашивать связанные сущности, и создайте классы данных для представления желаемого результата.
Определите взаимоотношения
Для определения связи «многие ко многим» сначала создайте класс для каждой из двух ваших сущностей. Связи «многие ко многим» отличаются от других типов связей тем, что в дочерней сущности обычно нет ссылки на родительскую сущность. Вместо этого создайте третий класс, представляющий ассоциативную сущность или таблицу перекрестных ссылок между двумя сущностями. Таблица перекрестных ссылок должна содержать столбцы для первичного ключа каждой сущности в связи «многие ко многим», представленной в таблице. В этом примере каждая строка в таблице перекрестных ссылок соответствует паре экземпляра Playlist и экземпляра Song , где ссылочный плейлист включает ссылочную песню.
@Entity data class Playlist( @PrimaryKey val playlistId: 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 )
Запрос к сущностям
Следующий шаг зависит от того, как вы хотите запрашивать информацию об этих связанных сущностях.
- Если вы хотите получить список плейлистов и соответствующих им песен , создайте новый класс данных, содержащий один объект
Playlistи список объектовSong, которые входят в состав плейлиста. - Если вы хотите получить список песен и соответствующих плейлистов для каждой песни, создайте новый класс данных, содержащий один объект
Songи список объектовPlaylist, включающих эту песню.
В любом случае, смоделируйте взаимосвязь между сущностями, используя свойство associateBy в аннотации @Relation в каждом из этих классов, чтобы определить сущность перекрестной ссылки, обеспечивающую связь между сущностью Playlist и сущностью Song .
data class PlaylistWithSongs( @Embedded val playlist: Playlist, @Relation( parentColumns = ["playlistId"], entityColumns = ["songId"], associateBy = Junction(PlaylistSongCrossRef::class) ) val songs: List<Song> ) data class SongWithPlaylists( @Embedded val song: Song, @Relation( parentColumns = ["songId"], entityColumns = ["playlistId"], associateBy = Junction(PlaylistSongCrossRef::class) ) val playlists: List<Playlist> )
Наконец, добавьте функцию в класс объекта доступа к данным (DAO), чтобы предоставить доступ к функции запроса, необходимой вашему приложению.
-
getPlaylistsWithSongs - Выполняет запрос к базе данных и возвращает все полученные объекты
PlaylistWithSongs. -
getSongsWithPlaylists - Выполняет запрос к базе данных и возвращает все полученные объекты
SongWithPlaylists.
Для выполнения двух запросов каждой функции требуется Room. Добавьте аннотацию @Transaction к обеим функциям, чтобы обеспечить атомарное выполнение операции.
@Transaction @Query("SELECT * FROM Playlist") suspend fun getPlaylistsWithSongs(): List<PlaylistWithSongs> @Transaction @Query("SELECT * FROM Song") suspend fun getSongsWithPlaylists(): List<SongWithPlaylists>
Составные ключи
Если вы определяете связь с использованием составных ключей, укажите несколько столбцов в parentColumns и entityColumns аннотации @Relation .
Если вам необходимо указать столбцы в Junction , используйте также parentColumns и entityColumns в аннотации Junction .
В следующем примере Playlist имеет составной первичный ключ, состоящий из playlistId и creatorId . Таблица перекрестных ссылок PlaylistSongCrossRef также включает эти столбцы для ссылки на плейлист.
@Entity(primaryKeys = ["playlistId", "creatorId"]) data class Playlist( val playlistId: Long, val creatorId: Long, val playlistName: String ) @Entity data class Song( @PrimaryKey val songId: Long, val songName: String, val artist: String ) @Entity(primaryKeys = ["playlistId", "creatorId", "songId"]) data class PlaylistSongCrossRef( val playlistId: Long, val creatorId: Long, val songId: Long ) data class PlaylistWithSongs( @Embedded val playlist: Playlist, @Relation( parentColumns = ["playlistId", "creatorId"], entityColumns = ["songId"], associateBy = Junction(PlaylistSongCrossRef::class) ) val songs: List<Song> )