O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Definir relações entre objetos

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 objetos Song 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 objetos Playlist 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 objetos PlaylistWithSongs resultantes.
  • getSongsWithPlaylists: este método consulta o banco de dados e retorna todos os objetos SongWithPlaylists 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.

UserWithPlaylistsAndSong modela a relação entre Usuário e PlaylistWithSong, que por sua vez modela a relação entre Playlist e Música.

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

Blog (em inglês)