A fim de interagir com os dados armazenados ao usar a biblioteca de persistência do Room para armazenar dados do app, defina objetos de acesso a dados (DAOs na sigla em inglês). Cada DAO inclui métodos que oferecem acesso abstrato ao banco de dados do app. Durante a compilação, o Room gera automaticamente implementações dos DAOs definidos.
Ao usar DAOs para acessar o banco de dados do app em vez de consultas diretas ou builders de consulta, é possível preservar a separação de conceitos, um princípio essencial de arquitetura. Os DAOs também facilitam a simulação do acesso ao banco de dados ao testar o app.
Anatomia de um DAO
É possível definir cada DAO como uma interface ou uma classe abstrata. As interfaces
geralmente são usadas para casos de uso básicos. De qualquer forma, é sempre preciso
adicionar a anotação @Dao
aos DAOs. Eles
não têm propriedades, mas definem um ou mais métodos para interagir
com os dados no banco de dados do app.
O código abaixo é um exemplo de um DAO simples que define métodos para
inserir, excluir e selecionar objetos User
em um banco de dados do Room:
Kotlin
@Dao interface UserDao { @Insert fun insertAll(vararg users: User) @Delete fun delete(user: User) @Query("SELECT * FROM user") fun getAll(): List<User> }
Java
@Dao public interface UserDao { @Insert void insertAll(User... users); @Delete void delete(User user); @Query("SELECT * FROM user") List<User> getAll(); }
Existem dois tipos de métodos DAO que definem interações de banco de dados:
- Métodos de conveniência, que permitem inserir, atualizar e excluir linhas no banco de dados sem programar códigos SQL.
- Métodos de consulta, que permitem criar sua própria consulta SQL para interagir com o banco de dados.
As seções abaixo demonstram como usar os dois tipos de métodos DAO para definir as interações de banco de dados necessárias do app.
Métodos de conveniência
O Room oferece anotações de conveniência para definir métodos de inserção, atualização e exclusão simples sem que você precise programar uma instrução SQL.
Caso precise definir inserções, atualizações ou exclusões mais complexas ou consultar os dados no banco de dados, use um método de consulta.
Inserir
A anotação @Insert
permite definir
métodos que inserem os parâmetros na tabela adequada no banco de dados. O código abaixo mostra exemplos de métodos @Insert
válidos que
inserem um ou mais objetos User
no banco de dados:
Kotlin
@Dao interface UserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertUsers(vararg users: User) @Insert fun insertBothUsers(user1: User, user2: User) @Insert fun insertUsersAndFriends(user: User, friends: List<User>) }
Java
@Dao public interface UserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) public void insertUsers(User... users); @Insert public void insertBothUsers(User user1, User user2); @Insert public void insertUsersAndFriends(User user, List<User> friends); }
Cada parâmetro de um método @Insert
precisa ser uma instância de uma classe
de entidade de dados do Room com a anotação
@Entity
ou uma coleção de instâncias de classe de entidade de dados, cada uma
apontando para um banco de dados. Quando um método @Insert
é chamado, o Room insere cada
instância de entidade transmitida na tabela de banco de dados correspondente.
Se o método @Insert
receber um único parâmetro, ele pode retornar um valor long
,
que é o novo rowId
do item inserido. Se o parâmetro for uma
matriz ou coleção, o método retorna uma matriz ou coleção de valores
long
, sendo que cada valor corresponde ao rowId
de um dos itens
inseridos. Para saber mais sobre como retornar valores rowId
, consulte a documentação
de referência da anotação @Insert
,
e também a documentação do SQLite para tabelas
rowid (em inglês).
Atualizar
A anotação @Update
permite
definir métodos que atualizam linhas específicas em uma tabela de banco de dados. Assim como
os métodos @Insert
, os @Update
aceitam instâncias de entidade de dados como parâmetros.
O código abaixo mostra um exemplo de um método @Update
que tenta atualizar
um ou mais objetos User
no banco de dados:
Kotlin
@Dao interface UserDao { @Update fun updateUsers(vararg users: User) }
Java
@Dao public interface UserDao { @Update public void updateUsers(User... users); }
O Room usa a chave primária para encontrar as linhas no banco de dados correspondentes às instâncias de entidade transmitidas. Caso não haja uma linha com a mesma chave primária, o Room não faz nenhuma modificação.
Um método @Update
tem a opção de retornar um valor int
para indicar o número de
linhas que foram corretamente atualizadas.
Excluir
A anotação @Delete
permite
definir métodos que excluem linhas específicas de uma tabela de banco de dados. Assim como
os métodos @Insert
, os @Delete
aceitam instâncias de entidade de dados como parâmetros.
O código abaixo mostra um exemplo de um método @Delete
que tenta excluir
um ou mais objetos User
do banco de dados:
Kotlin
@Dao interface UserDao { @Delete fun deleteUsers(vararg users: User) }
Java
@Dao public interface UserDao { @Delete public void deleteUsers(User... users); }
O Room usa a chave primária para encontrar as linhas no banco de dados correspondentes às instâncias de entidade transmitidas. Caso não haja uma linha com a mesma chave primária, o Room não faz nenhuma modificação.
Um método @Delete
tem a opção de retornar um valor int
para indicar o número de
linhas que foram corretamente excluídas.
Métodos de consulta
A anotação @Query
permite
criar instruções SQL e expor essas instruções como métodos DAO. Use-os para
consultar dados no banco do app ou quando for necessário realizar inserções, atualizações
e exclusões mais complexas.
O Room valida consultas SQL durante a compilação. Isso significa que, se houver um problema com a consulta, um erro de compilação é gerado, em vez de uma falha durante a execução.
Consultas simples
O código abaixo define um método que usa uma consulta SELECT
simples para retornar
todos os objetos User
do banco de dados:
Kotlin
@Query("SELECT * FROM user") fun loadAllUsers(): Array<User>
Java
@Query("SELECT * FROM user") public User[] loadAllUsers();
As seções abaixo demonstram como modificar esse exemplo para casos de uso típicos.
Retornar um subconjunto das colunas de uma tabela
Na maioria das vezes, você só precisa retornar um subconjunto de colunas da tabela consultada. Por exemplo, a interface pode exibir apenas o nome e o sobrenome de um usuário, em vez de todos os detalhes sobre ele. Para economizar recursos e otimizar a execução, consulte apenas os campos necessários.
O Room permite que você retorne qualquer objeto de consultas, desde que o conjunto de colunas de resultados possa ser mapeado para o objeto retornado. Por exemplo, você pode definir o objeto abaixo para armazenar o nome e o sobrenome de um usuário:
Kotlin
data class NameTuple( @ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String? )
Java
public class NameTuple { @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") @NonNull public String lastName; }
Em seguida, você pode retornar esse objeto simples do método de consulta:
Kotlin
@Query("SELECT first_name, last_name FROM user") fun loadFullName(): List<NameTuple>
Java
@Query("SELECT first_name, last_name FROM user") public List<NameTuple> loadFullName();
O Room entende que a consulta retorna valores para as colunas de first_name
e
last_name
e que esses valores podem ser mapeados para os campos da
classe NameTuple
. Se a consulta retornar uma coluna que não seja mapeada para um campo
no objeto retornado, o Room exibe um aviso.
Transmitir parâmetros simples para uma consulta
Na maioria das vezes, os métodos DAO precisam aceitar parâmetros para realizar operações de filtragem. O Room oferece suporte ao uso de parâmetros de método como parâmetros de vinculação nas consultas.
Por exemplo, o código abaixo define um método que retorna todos os usuários acima de uma determinada idade:
Kotlin
@Query("SELECT * FROM user WHERE age > :minAge") fun loadAllUsersOlderThan(minAge: Int): Array<User>
Java
@Query("SELECT * FROM user WHERE age > :minAge") public User[] loadAllUsersOlderThan(int minAge);
Também é possível transmitir vários parâmetros ou referenciar o mesmo parâmetro várias vezes em uma consulta, conforme mostrado no snippet de código abaixo.
Kotlin
@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge") fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User> @Query("SELECT * FROM user WHERE first_name LIKE :search " + "OR last_name LIKE :search") fun findUserWithName(search: String): List<User>
Java
@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge") public User[] loadAllUsersBetweenAges(int minAge, int maxAge); @Query("SELECT * FROM user WHERE first_name LIKE :search " + "OR last_name LIKE :search") public List<User> findUserWithName(String search);
Transmitir um conjunto de parâmetros para uma consulta
Alguns dos métodos DAO podem exigir que você transmita um número variável de parâmetros não conhecidos até o momento da execução. O Room entende quando um parâmetro representa uma coleção e o expande automaticamente durante a execução, com base no número de parâmetros fornecidos.
Por exemplo, o código abaixo define um método que retorna informações sobre todos os usuários de um subconjunto de regiões:
Kotlin
@Query("SELECT * FROM user WHERE region IN (:regions)") fun loadUsersFromRegions(regions: List<String>): List<User>
Java
@Query("SELECT * FROM user WHERE region IN (:regions)") public List<User> loadUsersFromRegions(List<String> regions);
Consulta de várias tabelas
Algumas consultas podem exigir acesso a várias tabelas para calcular o
resultado. É possível usar cláusulas JOIN
nas consultas SQL para referenciar mais de
uma tabela.
O código abaixo define um método que une três tabelas para retornar os livros que um usuário específico pegou emprestados:
Kotlin
@Query( "SELECT * FROM book " + "INNER JOIN loan ON loan.book_id = book.id " + "INNER JOIN user ON user.id = loan.user_id " + "WHERE user.name LIKE :userName" ) fun findBooksBorrowedByNameSync(userName: String): List<Book>
Java
@Query("SELECT * FROM book " + "INNER JOIN loan ON loan.book_id = book.id " + "INNER JOIN user ON user.id = loan.user_id " + "WHERE user.name LIKE :userName") public List<Book> findBooksBorrowedByNameSync(String userName);
Também é possível definir objetos simples para retornar um subconjunto de colunas de várias tabelas mescladas, conforme discutido em Retornar um subconjunto de colunas de uma tabela. O código abaixo define um DAO com um método que retorna os nomes dos usuários e os nomes dos livros que eles pegaram emprestados:
Kotlin
interface UserBookDao { @Query( "SELECT user.name AS userName, book.name AS bookName " + "FROM user, book " + "WHERE user.id = book.user_id" ) fun loadUserAndBookNames(): LiveData<List<UserBook>> // You can also define this class in a separate file. data class UserBook(val userName: String?, val bookName: String?) }
Java
@Dao public interface UserBookDao { @Query("SELECT user.name AS userName, book.name AS bookName " + "FROM user, book " + "WHERE user.id = book.user_id") public LiveData<List<UserBook>> loadUserAndBookNames(); // You can also define this class in a separate file, as long as you add the // "public" access modifier. static class UserBook { public String userName; public String bookName; } }
Retornar um multimapa
No Room 2.4 e versões mais recentes, também é possível consultar colunas de várias tabelas sem definir uma classe de dados extra, criando métodos de consulta que retornem um multimapa (link em inglês).
Considere o exemplo na seção Consultar várias tabelas.
Em vez de retornar uma lista de instâncias de uma classe de dados personalizada que contém
pares de instâncias User
e Book
, é possível retornar um mapeamento de User
e
Book
diretamente do método de consulta:
Kotlin
@Query( "SELECT * FROM user" + "JOIN book ON user.id = book.user_id" ) fun loadUserAndBookNames(): Map<User, List<Book>>
Java
@Query( "SELECT * FROM user" + "JOIN book ON user.id = book.user_id" ) public Map<User, List<Book>> loadUserAndBookNames();
Quando o método de consulta retorna um multimapa, é possível criar consultas que usam
cláusulas GROUP BY
, o que permite que você aproveite os recursos do SQL para
cálculos e filtros avançados. Por exemplo, você pode modificar o
método loadUserAndBookNames()
para retornar apenas usuários com três ou mais livros
emprestados:
Kotlin
@Query( "SELECT * FROM user" + "JOIN book ON user.id = book.user_id" + "GROUP BY user.name WHERE COUNT(book.id) >= 3" ) fun loadUserAndBookNames(): Map<User, List<Book>>
Java
@Query( "SELECT * FROM user" + "JOIN book ON user.id = book.user_id" + "GROUP BY user.name WHERE COUNT(book.id) >= 3" ) public Map<User, List<Book>> loadUserAndBookNames();
Caso não precise mapear objetos inteiros, também é possível retornar mapeamentos entre
colunas específicas na consulta, definindo os atributos
keyColumn
e
valueColumn
em uma anotação @MapInfo
no
método de consulta:
Kotlin
@MapInfo(keyColumn = "userName", valueColumn = "bookName") @Query( "SELECT user.name AS username, book.name AS bookname FROM user" + "JOIN book ON user.id = book.user_id" ) fun loadUserAndBookNames(): Map<String, List<String>>
Java
@MapInfo(keyColumn = "userName", valueColumn = "bookName") @Query( "SELECT user.name AS username, book.name AS bookname FROM user" + "JOIN book ON user.id = book.user_id" ) public Map<String, List<String>> loadUserAndBookNames();
Tipos de retorno especiais
O Room oferece alguns tipos de retorno especiais para integração com outras bibliotecas de API.
Consultas paginadas com a biblioteca Paging
O Room oferece suporte a consultas paginadas usando a integração com a biblioteca
Paging. No Room 2.3.0-alpha01 e
versões mais recentes, os DAOs podem retornar objetos
PagingSource
para uso
com a Paging 3.
Kotlin
@Dao interface UserDao { @Query("SELECT * FROM users WHERE label LIKE :query") fun pagingSource(query: String): PagingSource<Int, User> }
Java
@Dao interface UserDao { @Query("SELECT * FROM users WHERE label LIKE :query") PagingSource<Integer, User> pagingSource(String query); }
Veja mais informações sobre como escolher parâmetros de tipo para um PagingSource
em
Selecionar tipos de chave e
valor.
Acesso direto com cursor
Se a lógica do app exigir acesso direto às linhas de retorno, programe
os métodos DAO para retornar um objeto Cursor
,
conforme mostrado no exemplo abaixo.
Kotlin
@Dao interface UserDao { @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5") fun loadRawUsersOlderThan(minAge: Int): Cursor }
Java
@Dao public interface UserDao { @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5") public Cursor loadRawUsersOlderThan(int minAge); }
Outros recursos
Para saber mais sobre como acessar dados usando DAOs do Room, consulte os recursos abaixo:
Exemplos
- Android Sunflower (em inglês)