Em uma relação de muitos para muitos entre duas entidades, cada instância da entidade pai corresponde a zero ou mais instâncias da entidade filha. O inverso também é verdadeiro.
No exemplo do app de streaming de música, considere as músicas nas playlists definidas pelo usuário. Cada playlist pode incluir muitas músicas, e cada música pode fazer parte de muitas playlists. Portanto, há uma relação de muitos para muitos entre as entidades Playlist e Song.
Siga estas etapas para definir e consultar relações de muitos para muitos no seu banco de dados:
- Defina a relação: estabeleça as entidades e a entidade associativa, ou tabela de referência cruzada, para representar a relação de muitos para muitos.
- Consulte as entidades: determine como você quer consultar as entidades relacionadas e crie classes de dados para representar a saída pretendida.
Definir a relação
Para definir uma relação de muitos para muitos, crie uma classe para cada entidade. As relações de muitos para muitos são diferentes de outros tipos de relacionamento porque geralmente não há referência à entidade pai na entidade filha. Em vez disso, crie uma terceira classe para representar uma
entidade associativa, ou tabela de referência cruzada, entre as
duas entidades. A tabela de referência cruzada precisa ter colunas para a chave primária de cada entidade no relacionamento de muitos para muitos representado na tabela. Neste exemplo, cada linha na tabela de referência cruzada corresponde a um par de uma instância Playlist e uma instância Song em que a playlist referenciada inclui a música referenciada.
@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 )
Consultar as entidades
A próxima etapa depende de como você quer consultar as entidades relacionadas.
- Caso queira consultar playlists e uma lista das músicas correspondentes em cada playlist, crie uma nova classe de dados que contenha um único objeto
Playliste uma lista dos objetosSongque a playlist inclui. - Caso queira consultar músicas e uma lista das playlists correspondentes para cada música, crie uma nova classe de dados que contenha um único objeto
Songe uma lista dos objetosPlaylistque incluem a música.
Nos dois casos, modele o relacionamento entre as entidades usando a
associateBy propriedade na @Relation anotação em cada uma dessas
classes para identificar a entidade de referência cruzada que fornece a relação
entre a entidade Playlist e a entidade 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> )
Por fim, adicione uma função à classe de objeto de acesso a dados (DAO, na sigla em inglês) para expor a função de consulta que seu app precisa.
getPlaylistsWithSongs- Consulta o banco de dados e retorna todos os objetos
PlaylistWithSongsresultantes. getSongsWithPlaylists- Consulta o banco de dados e retorna todos os objetos
SongWithPlaylistsresultantes.
Cada função exige que o Room execute duas consultas. Adicione a @Transaction
anotação às duas funções para garantir que a operação seja executada atomicamente.
@Transaction @Query("SELECT * FROM Playlist") suspend fun getPlaylistsWithSongs(): List<PlaylistWithSongs> @Transaction @Query("SELECT * FROM Song") suspend fun getSongsWithPlaylists(): List<SongWithPlaylists>
Chaves compostas
Se você definir a relação usando chaves compostas, especifique várias colunas em parentColumns e entityColumns da anotação @Relation.
Se você precisar especificar colunas na Junction, use parentColumns e entityColumns na anotação Junction também.
No exemplo a seguir, Playlist tem uma chave primária composta por playlistId e creatorId. A tabela de referência cruzada PlaylistSongCrossRef também inclui essas colunas para referenciar a playlist.
@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> )