Como o SQLite é um banco de dados relacional, é possível especificar relações entre entidades. Embora a maior parte das bibliotecas de mapeamento relacional de objetos permita que objetos de entidades se referenciem mutuamente, o Room proíbe isso explicitamente. Para saber mais sobre o raciocínio técnico por trás dessa decisão, consulte Entender por que o Room não permite referências de objetos.
Criar objetos incorporados
Às vezes, você quer expressar uma entidade ou um objeto de dados como um todo coeso na lógica do banco de dados, mesmo que o objeto tenha vários campos. Nessas situações, você pode usar a anotação @Embedded
para representar um objeto que você gostaria de decompor nos subcampos em uma tabela. Em seguida, você pode consultar os campos incorporados da mesma forma que faria para outras colunas individuais.
Por exemplo, sua classe User
pode incluir um campo do tipo Address
, que representa uma composição de campos nomeados street
, city
, state
e postCode
. Para armazenar as colunas compostas separadamente na tabela, inclua o campo Address
na classe User
que é anotada com @Embedded
, conforme mostrado no snippet de código a seguir.
Kotlin
data class Address( val street: String?, val state: String?, val city: String?, @ColumnInfo(name = "post_code") val postCode: Int ) @Entity data class User( @PrimaryKey val id: Int, val firstName: String?, @Embedded val address: Address? )
Java
public class Address { public String street; public String state; public String city; @ColumnInfo(name = "post_code") public int postCode; } @Entity public class User { @PrimaryKey public int id; public String firstName; @Embedded public Address address; }
A tabela que representa um objeto User
contém colunas com os seguintes nomes: id
, firstName
, street
, state
, city
e post_code
.
Se uma entidade tem vários campos incorporados do mesmo tipo, é possível manter cada coluna única, definindo a propriedade prefix
. O Room adiciona o valor fornecido ao início do nome de cada coluna no objeto incorporado.
Definir as relações de um para um
Uma relação de um para um entre duas entidades é uma relação em que cada instância da entidade pai corresponde exatamente a uma instância da entidade filha e vice-versa.
Por exemplo, considere um app de streaming de música em que o usuário tem uma biblioteca de músicas que pertencem a ele. Cada usuário tem apenas uma biblioteca, e cada biblioteca corresponde exatamente a um usuário. Portanto, há uma relação de um para um entre a entidade User
e a entidade Library
.
Primeiro, crie uma classe para cada uma da duas entidades. Uma das entidades precisa incluir uma variável que seja uma referência à chave principal da outra entidade.
Kotlin
@Entity data class User( @PrimaryKey val userId: Long, val name: String, val age: Int ) @Entity data class Library( @PrimaryKey val libraryId: Long, val userOwnerId: Long )
Java
@Entity public class User { @PrimaryKey public long userId; public String name; public int age; } @Entity public class Library { @PrimaryKey public long libraryId; public long userOwnerId; }
Para consultar a lista de usuários e bibliotecas correspondentes, primeiro você precisa modelar a relação de um para um entre as duas entidades. Para fazer isso, crie uma nova classe de dados em que cada instância contém uma instância da entidade pai e a instância correspondente da entidade filha. Adicione a anotação @Relation
à instância da entidade filha, com parentColumn
definida como o nome da coluna de chave primária da entidade pai e entityColumn
definida com o nome da coluna da entidade filha que faz referência à chave primária da entidade pai.
Kotlin
data class UserAndLibrary( @Embedded val user: User, @Relation( parentColumn = "userId", entityColumn = "userOwnerId" ) val library: Library )
Java
public class UserAndLibrary { @Embedded public User user; @Relation( parentColumn = "userId", entityColumn = "userOwnerId" ) public Library library; }
Por fim, adicione um método à classe DAO que retorna todas as instâncias da classe de dados que emparelha a entidade pai e a entidade filha. Esse método requer espaço para executar duas consultas. Portanto, adicione a anotação @Transaction
a esse método para garantir que toda a operação seja realizada atomicamente.
Kotlin
@Transaction @Query("SELECT * FROM User") fun getUsersAndLibraries(): List<UserAndLibrary>
Java
@Transaction @Query("SELECT * FROM User") public List<UserAndLibrary> getUsersAndLibraries();
Definir relações "um para muitos"
Uma relação de um para muitos entre duas entidades é uma relação em que cada instância da entidade pai corresponde a zero ou mais instâncias da entidade filha, mas cada instância da entidade filha só pode corresponder exatamente a uma instância da entidade pai.
No exemplo do app de streaming de música, suponha que o usuário consiga organizar suas músicas em playlists. Cada usuário pode criar quantas playlists quiser, mas cada playlist é criada por exatamente um usuário. Portanto, há uma relação de um para muitos entre a entidade User
e a entidade Playlist
.
Primeiro, crie uma classe para cada uma da duas entidades. Como no exemplo anterior, a entidade filha precisa incluir uma variável que seja uma referência à chave primária da entidade pai.
Kotlin
@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 )
Java
@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; }
Para consultar a lista de usuários e listas de reprodução correspondentes, primeiro você precisa modelar a relação um para um entre as duas entidades. Para fazer isso, crie uma nova classe de dados em que cada instância contém uma instância da entidade pai e uma lista de todas as instâncias de entidade filha correspondentes. Adicione a anotação @Relation
à instância da entidade filha, com parentColumn
definida como o nome da coluna de chave primária da entidade pai e entityColumn
definida com o nome da coluna da entidade filha que faz referência à chave primária da entidade pai.
Kotlin
data class UserWithPlaylists( @Embedded val user: User, @Relation( parentColumn = "userId", entityColumn = "userCreatorId" ) val playlists: List<Playlist> )
Java
public class UserWithPlaylists { @Embedded public User user; @Relation( parentColumn = "userId", entityColumn = "userCreatorId" ) public List<Playlist> playlists; }
Por fim, adicione um método à classe DAO que retorna todas as instâncias da classe de dados que emparelha a entidade pai e a entidade filha. Esse método requer espaço para executar duas consultas. Portanto, adicione a anotação @Transaction
a esse método para garantir que toda a operação seja realizada atomicamente.
Kotlin
@Transaction @Query("SELECT * FROM User") fun getUsersWithPlaylists(): List<UserWithPlaylists>
Java
@Transaction @Query("SELECT * FROM User") public List<UserWithPlaylists> getUsersWithPlaylists();
Definir relações "muitos para muitos"
Uma relação de muitos para muitos entre duas entidades é uma relação em que cada instância da entidade pai corresponde a zero ou mais instâncias da entidade filha e vice-versa.
No exemplo do app de streaming de música, considere novamente as 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
.
Primeiro, crie uma classe para cada uma da duas entidades. 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 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; }
A próxima etapa depende de como você quer consultar essas entidades relacionadas.
- Se quiser consultar playlists e uma lista das músicas correspondentes para cada playlist, crie uma nova classe de dados que contenha um único objeto
Playlist
e uma lista de todos dos objetosSong
que a playlist inclui. - Se você quiser consultar músicas e uma lista das playlists, crie uma nova classe de dados que contenha um
Song
e uma lista de todos os objetosPlaylist
em que a música está incluída.
Nos dois casos, modele o relacionamento 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 a entidade Playlist
e a entidade 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 funcionalidade de consulta que seu app precisa.
getPlaylistsWithSongs
: este método consulta o banco de dados e retorna todos os objetosPlaylistWithSongs
resultantes.getSongsWithPlaylists
: este método consulta o banco de dados e retorna todos os objetosSongWithPlaylists
resultantes.
Todos esses 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();
Definir relações aninhadas
Às vezes, pode ser necessário consultar um conjunto de três ou mais tabelas relacionadas entre si. Nesse caso, você definiria relações aninhadas entre as tabelas.
Suponha que, no exemplo do app de streaming de música, você queira consultar todos os usuários, todas as playlists de cada usuário e todas as músicas em cada playlist de cada usuário. Usuários têm uma relação de um para muitos com playlists, e listas de reprodução têm uma relação de muitos para muitos com músicas. O exemplo de código a seguir mostra as classes que representam essas entidades, bem como a tabela de referência cruzada para a relação de muitos para muitos entre listas de reprodução e músicas:
Kotlin
@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 )
Java
@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; }
Primeiro, modele a relação entre duas tabelas no seu conjunto como você faria normalmente, com uma classe de dados e a anotação @Relation
. O exemplo a seguir mostra uma classe PlaylistWithSongs
que modela uma relação de muitos para muitos entre a classe de entidade Playlist
e a classe de entidade Song
:
Kotlin
data class PlaylistWithSongs( @Embedded val playlist: Playlist, @Relation( parentColumn = "playlistId", entityColumn = "songId", associateBy = @Junction(PlaylistSongCrossRef::class) ) val songs: List<Song> )
Java
public class PlaylistWithSongs { @Embedded public Playlist playlist; @Relation( parentColumn = "playlistId", entityColumn = "songId", associateBy = @Junction(PlaylistSongCrossRef.class) ) public List<Song> songs; }
Depois de definir uma classe de dados que representa essa relação, crie outra classe de dados que modele a relação entre outra tabela do conjunto e a classe de relação primária, "aninhando" a relação existente com a nova. O exemplo a seguir mostra uma classe UserWithPlaylistsAndSongs
que modela uma relação de um para muitos entre a classe de entidade User
e a classe de relação PlaylistWithSongs
:
Kotlin
data class UserWithPlaylistsAndSongs { @Embedded val user: User @Relation( entity = Playlist::class, parentColumn = "userId", entityColumn = "userCreatorId" ) val playlists: List<PlaylistWithSongs> }
Java
public class UserWithPlaylistsAndSongs { @Embedded public User user; @Relation( entity = Playlist.class, parentColumn = "userId", entityColumn = "userCreatorId" ) public List<PlaylistWithSongs> playlists; }
A classe UserWithPlaylistsAndSongs
indiretamente modela as relações entre as três classes de entidade: User
, Playlist
e Song
. Isso é ilustrado na figura 1.

Figura 1. Diagrama de classes de relacionamento no exemplo do app de streaming de música.
Se houver mais tabelas no seu conjunto, crie uma classe para modelar a relação entre cada tabela restante e a classe de relação que modela as relações entre todas as tabelas anteriores. Isso cria uma cadeia de relações aninhadas entre todas as tabelas que você quer consultar.
Por fim, adicione um método à classe DAO para expor a funcionalidade de consulta que seu app precisa. Este método requer espaço para executar várias consultas. Portanto, adicione a anotação @Transaction
para garantir que toda a operação seja realizada atomicamente:
Kotlin
@Transaction @Query("SELECT * FROM User") fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>
Java
@Transaction @Query("SELECT * FROM User") public List<UserWithPlaylistsAndSongs> getUsersWithPlaylistsAndSongs();
Outros recursos
Para saber mais sobre como definir relações entre entidades no Room, consulte os recursos adicionais a seguir.
Amostras (em inglês)
Vídeo
- O que há de novo no Room (Conferência de Desenvolvedores Android, 2019)