Definir e consultar relações de muitos para muitos

Em ums 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 diferentes. Portanto, há uma relação de muitos para muitos entre a entidade Playlist e a entidade Song.

Siga estas etapas para definir e consultar relações "muitos para muitos" no seu banco de dados:

  1. Definir a relação: estabeleça as entidades e a entidade associativa (tabela de referência cruzada) para representar a relação de muitos para muitos.
  2. Consultar 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 mãe na entidade filha. Em vez disso, crie uma terceira classe para representar uma entidade associativa (link em inglês) ou uma tabela de referência cruzada (link em inglês) entre as duas entidades. A tabela de referência cruzada precisa ter colunas para a chave primária de cada entidade na relação de muitos para muitos representada 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 música referenciada está incluída na playlist referenciada.

Kotlin

@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"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

Java

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    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;
}

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 Playlist e uma lista de todos os objetos Song que a playlist inclui.
  • Se você quiser consultar músicas e uma lista das playlists correspondentes para cada uma, crie uma nova classe de dados que contenha um único objeto Song e uma lista de todos os objetos Playlist em que a música está incluída.

Nos dois casos, modele a relação entre as entidades usando a propriedade associateBy na anotação @Relation em cada uma dessas classes para identificar a entidade de referência cruzada que fornece a relação entre as entidades Playlist e Song.

Kotlin

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

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

Java

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

public class SongWithPlaylists {
    @Embedded public Song song;
    @Relation(
         parentColumn = "songId",
         entityColumn = "playlistId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Playlist> playlists;
}

Por fim, adicione um método à classe DAO para expor a função de consulta de que o app precisa.

  • getPlaylistsWithSongs: esse método consulta o banco de dados e retorna todos os objetos PlaylistWithSongs resultantes.
  • getSongsWithPlaylists: esse método consulta o banco de dados e retorna todos os objetos SongWithPlaylists resultantes.

Ambos métodos exigem que o Room execute duas consultas. Portanto, adicione a anotação @Transaction aos dois métodos para garantir que toda a operação seja realizada atomicamente.

Kotlin

@Transaction
@Query("SELECT * FROM Playlist")
fun getPlaylistsWithSongs(): List<PlaylistWithSongs>

@Transaction
@Query("SELECT * FROM Song")
fun getSongsWithPlaylists(): List<SongWithPlaylists>

Java

@Transaction
@Query("SELECT * FROM Playlist")
public List<PlaylistWithSongs> getPlaylistsWithSongs();

@Transaction
@Query("SELECT * FROM Song")
public List<SongWithPlaylists> getSongsWithPlaylists();