Room: o que é, biblioteca ORM e trabalho com SQLite

Autor: IT Sectr Publicado: 2026-03-12 Tempo de leitura: 10 min

Room é uma biblioteca ORM do Android Jetpack que fornece uma camada de abstração sobre o SQLite para trabalhar com bancos de dados locais no Android. De acordo com a documentação oficial em Android Developers, 2025, o Room gera automaticamente implementações de DAO baseadas em anotações em tempo de compilação, eliminando cerca de 70% do código repetitivo em comparação com o uso direto do SQLiteOpenHelper. A biblioteca realiza validação de consultas SQL em tempo de compilação, permitindo detectar erros de sintaxe antes de executar o aplicativo em um dispositivo.

Pontos Principais

  • Room é uma biblioteca ORM do Android Jetpack que fornece uma camada de abstração sobre SQLite para armazenamento local de dados em aplicações Android.
  • Três componentes principais: Entity (definição de tabela), DAO (operações de dados) e Database (ponto de entrada do banco de dados).
  • A validação de consultas SQL em tempo de compilação é uma vantagem chave, permitindo encontrar erros antes da instalação do aplicativo.
  • Suporte integrado para Flow, LiveData e RxJava para observação reativa de mudanças no banco de dados.
  • O mecanismo de migrações permite atualizar o esquema do banco de dados sem perder dados de usuário já salvos.

O que é a biblioteca Room ORM?

Room é uma biblioteca ORM do Android Jetpack criada pelo Google para simplificar o trabalho com bancos de dados SQLite locais na plataforma Android. Ela fornece anotações para descrever o esquema de dados e gera automaticamente implementações de interfaces DAO em tempo de compilação. Ao contrário do uso direto do SQLiteOpenHelper, o Room libera o desenvolvedor de escrever uma quantidade significativa de código repetitivo para criar, abrir e gerenciar conexões com o banco de dados.

A biblioteca foi apresentada no Google I/O 2017 como parte dos componentes de arquitetura do Android. Desde então, o Room se tornou o padrão de fato para armazenamento local de dados, superando em popularidade soluções como GreenDAO e Realm para Android. De acordo com o Google, a biblioteca é usada em mais de 60% dos aplicativos publicados no Google Play que trabalham com dados locais no dispositivo.

A característica chave é a validação de consultas SQL em tempo de compilação usando um processador de anotações. Se um desenvolvedor cometer um erro em um comando SQL, por exemplo, especificando um nome de coluna inexistente, a compilação falhará com um erro antes da instalação do aplicativo. Isso é fundamentalmente diferente da abordagem do SQLiteOpenHelper, onde tais erros são detectados apenas em tempo de execução, frequentemente em produção.

TypeConverters para tipos não padronizados

SQLite suporta apenas cinco tipos de dados: TEXT, INTEGER, REAL, BLOB e NULL. No entanto, Java e Kotlin usam tipos complexos: Date, List, Enum e objetos personalizados. Para armazená-los, o Room fornece o mecanismo TypeConverters — métodos estáticos que convertem um tipo complexo em um tipo primitivo compreensível para SQLite. Por exemplo, um objeto Date é convertido para Long (timestamp), e List<String> para uma string JSON via Gson ou Moshi.

kotlin
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

val db = Room
    .databaseBuilder(context, AppDatabase::class.java, "app-db")
    .build()

Para declarar um conversor, basta adicionar a anotação @TypeConverter a um método estático e especificar a classe do conversor na anotação @TypeConverters no nível do banco de dados. O Room aplica automaticamente o conversor ao ler e escrever o tipo correspondente em cada consulta SQL sem chamar manualmente métodos de conversão.

Arquitetura do Room: três componentes chave

Room consiste em três componentes principais: Entity, DAO e Database. Cada um desempenha um papel estritamente definido e é anotado com a anotação correspondente. Juntos, eles formam uma camada completa de acesso a dados que isola a lógica de negócios da aplicação dos detalhes de implementação do SQLite.

Entity — Tabela do banco de dados

Entity é uma classe de dados que descreve a estrutura de uma tabela no banco de dados. Cada campo da classe corresponde a uma coluna da tabela, e cada linha no banco de dados corresponde a uma instância da classe. A anotação @Entity informa ao Room que a classe é uma tabela. O campo com a anotação @PrimaryKey define a chave primária, que pode ser auto-incrementada ou composta. @ForeignKey é usado para relacionamentos entre tabelas, garantindo a integridade dos dados no nível do banco de dados.

kotlin
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    @ColumnInfo(name = "full_name")
    val name: String,
    val age: Int,
    val email: String
)

DAO — Operações com dados

DAO (Data Access Object) é uma interface ou classe abstrata que declara operações para trabalhar com dados: inserir, ler, atualizar e excluir. Cada operação é anotada com @Insert, @Query, @Update ou @Delete. O Room gera automaticamente a implementação desta interface em tempo de compilação. A anotação @Query é particularmente valiosa — ela aceita uma consulta SQL como string e valida sua correção em tempo de compilação.

kotlin
@Dao
interface UserDao {
    @Insert
    suspend fun insert(user: User): Long

    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: Int): User?

    @Query("SELECT * FROM users")
    fun getAllUsers(): Flow<List<User>>

    @Delete
    suspend fun delete(user: User)
}

Database — Ponto de entrada

Database é uma classe abstrata que estende RoomDatabase e serve como ponto de entrada para o banco de dados. Ela contém uma lista de todas as Entities e fornece métodos abstratos para obter DAOs. A classe é anotada com @Database, que especifica a versão do esquema e a lista de entidades. A instância do banco de dados é criada via Room.databaseBuilder com o contexto da aplicação, nome do arquivo e classe Database.

Como o Room funciona com SQLite internamente

Room não substitui o SQLite, mas funciona sobre ele como uma camada de abstração. A arquitetura interna inclui um processador de anotações, gerador de código e pool de conexões. Em tempo de compilação, o processador de anotações analisa as classes Entity, DAO e Database, e então gera classes de implementação com o sufixo _Impl. Todas as classes geradas são colocadas no pacote de compilação e não são visíveis diretamente para o desenvolvedor.

A geração de código em tempo de compilação é o mecanismo central do Room. Para cada interface DAO, uma classe com a implementação completa de todos os métodos anotados é gerada. As consultas SQL da anotação @Query são validadas quanto à correção: o processador compara os nomes das colunas com os campos Entity e verifica a sintaxe SQL. Se um erro for encontrado, a compilação é interrompida com uma mensagem clara. Isso é impossível ao usar SQLiteOpenHelper diretamente, onde os erros só aparecem em tempo de execução.

Geração de código em tempo de compilação

O processo de geração inclui três etapas. Primeira — validação do esquema: o processador verifica se todas as classes listadas em @Database são Entities válidas. Segunda — geração do corpo do DAO: para cada método, uma implementação é criada usando o objeto interno RoomSQLiteQuery que executa consultas preparadas. Terceira — geração da classe Database_Impl, que lida com a criação e abertura do banco de dados, bem como a inicialização de todos os objetos DAO.

kotlin
class UserDao_Impl(private val __db: RoomDatabase) : UserDao {
    private val __insertionAdapter = __db
        .createInsertionAdapter(User::class, 0)

    override suspend fun insert(user: User): Long {
        __db.assertNotSuspendingTransaction()
        return __db.runInTransaction {
            __insertionAdapter.insertAndReturnId(user)
        }
    }
}

O Room não cria um pool de threads separado para operações de banco de dados. Por padrão, as consultas são executadas na thread chamadora com uma limitação: leitura e escrita bloqueiam a thread. Para trabalho assíncrono, o Room se integra com corrotinas Kotlin via funções suspend, com LiveData através de valores de retorno, e com Flow através de wrappers reativos. Isso dá ao desenvolvedor a flexibilidade de escolher a solução arquitetural para uma tarefa específica.

Exemplo de uso do Room em uma aplicação Android

Vamos ver um exemplo prático de criação de um aplicativo para armazenar notas usando Room. O aplicativo contém uma tabela Note com os campos id, title, content e timestamp. Os usuários podem adicionar, visualizar e excluir notas. Corrotinas são usadas para operações assíncronas.

Configuração de dependências Gradle

Para integrar o Room em um projeto Android, adicione dependências ao arquivo build.gradle do módulo do aplicativo. Room requer três componentes: a biblioteca de tempo de execução, o processador de anotações kapt e suporte opcional para corrotinas. A versão da biblioteca é especificada em uma variável room_version para facilitar a atualização. A partir do Room 2.4.0, o KSP é suportado como alternativa ao kapt com velocidades de compilação mais rápidas.

groovy
dependencies {
    def room_version = "2.6.1"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
    // Opcional: teste
    testImplementation "androidx.room:room-testing:$room_version"
}

Após configurar as dependências, crie três arquivos: a Entity Note, a interface NoteDao e a classe AppDatabase. A Entity Note contém campos com anotações @PrimaryKey e @ColumnInfo. O DAO fornece métodos para inserir, obter a lista e excluir. O Database vincula a Entity e o DAO através da anotação @Database.

kotlin
@Entity(tableName = "notes")
data class Note(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val title: String,
    val content: String,
    @ColumnInfo(name = "created_at")
    val timestamp: Long = System.currentTimeMillis()
)

@Dao
interface NoteDao {
    @Insert
    suspend fun insert(note: Note)

    @Query("SELECT * FROM notes ORDER BY created_at DESC")
    fun getAllNotes(): Flow<List<Note>>

    @Delete
    suspend fun delete(note: Note)
}

O arquivo AppDatabase é declarado como uma classe abstrata que estende RoomDatabase. A anotação @Database especifica todas as Entities para a versão atual e o número da versão do esquema. Para obter uma instância, o padrão singleton é usado através do método build do Room.databaseBuilder com o contexto da aplicação. O cache da instância do banco de dados evita múltiplas criações que podem levar a vazamentos de memória.

Migrações de banco de dados no Room

As Migrações no Room são um mecanismo para alterar o esquema do banco de dados ao atualizar um aplicativo sem perder dados existentes. Quando um usuário instala uma nova versão com Entities modificadas, o Room detecta a incompatibilidade de versão e executa as etapas de migração especificadas. Sem uma migração, o banco de dados será excluído e recriado, resultando na perda de todos os dados salvos pelo usuário.

Uma migração é descrita pela classe Migration, que recebe as versões inicial e final do banco de dados. Dentro do método migrate, uma consulta SQL ALTER TABLE ou CREATE TABLE é executada para alterar o esquema. Room não pode detectar automaticamente alterações de esquema — o desenvolvedor deve escrever uma migração manualmente para cada alteração de Entity. A partir do Room 2.4.0, o recurso experimental autoMigrations está disponível para geração automática de migrações.

Migrações automáticas com autoMigrations

O recurso autoMigrations permite que o Room gere automaticamente migrações com base nas diferenças entre versões de Entity. Para usá-lo, basta adicionar a anotação @AutoMigration ao @Database e habilitar a exportação do esquema para JSON. O Room compara os esquemas de versões adjacentes e gera as consultas ALTER necessárias. No entanto, autoMigrations suporta apenas alterações compatíveis com versões anteriores: adicionar colunas, criar índices e alterar tipos com transformações compatíveis.

kotlin
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(db: SupportSQLiteDatabase) {
        db.execSQL(
            "ALTER TABLE users ADD COLUMN phone TEXT"
        )
    }
}

val db = Room
    .databaseBuilder(context, AppDatabase::class.java, "app-db")
    .addMigrations(MIGRATION_1_2)
    .build()

Para alterações complexas, como renomear colunas ou mesclar tabelas, é necessária uma migração manual usando tabelas intermediárias. Um cenário típico: criar uma tabela temporária com o esquema antigo, copiar dados da tabela antiga para a nova com transformações, excluir a tabela antiga e renomear a temporária. Room garante que todas as migrações sejam executadas em uma única transação e, se ocorrer um erro, as alterações são completamente revertidas.

Perguntas Frequentes

Como o Room difere do SQLiteOpenHelper?

Room fornece uma abstração ORM com anotações e validação de SQL em tempo de compilação, enquanto o SQLiteOpenHelper requer escrever manualmente todas as consultas e gerenciar conexões. O Room gera automaticamente código para operações CRUD e se integra com componentes de arquitetura do Android, incluindo LiveData e Flow.

Quais tipos de dados o Room suporta?

Room suporta todos os tipos primitivos Java: Int, Long, Boolean, Float, Double, bem como String, ByteArray e Date. Para tipos complexos como List ou Enum, TypeConverters são usados — métodos de conversão estáticos que convertem tipos não padronizados em formatos compatíveis com SQLite.

Pode-se usar Room sem corrotinas?

Sim, Room suporta chamadas síncronas sem corrotinas, mas elas bloqueiam a thread em que são executadas. Para trabalho assíncrono, você pode usar LiveData ou RxJava em vez de corrotinas. O Google recomenda usar corrotinas como método principal para acesso assíncrono a dados em novos projetos.

O que acontece na ausência de migração?

Se Room detectar uma incompatibilidade de versão do banco de dados e não encontrar uma migração adequada, por padrão ele lança uma IllegalStateException com uma descrição do erro. O desenvolvedor pode substituir esse comportamento com o método fallbackToDestructiveMigration, que excluirá o banco de dados existente e criará um novo, perdendo todos os dados.

Como o Room lida com relacionamentos entre tabelas?

Room suporta relacionamentos através de objetos aninhados com a anotação @Embedded e através de classes de relacionamento com a anotação @Relation. Para consultas complexas envolvendo junções de tabelas, classes POJO personalizadas são usadas, cujos campos são preenchidos a partir dos resultados de @Query com instruções SQL JOIN.

Resumo

  • Room é uma biblioteca ORM do Android Jetpack que cria uma camada de abstração sobre SQLite para armazenamento conveniente de dados no dispositivo.
  • A arquitetura é baseada em três componentes: Entity (esquema de tabela), DAO (operações) e Database (ponto de entrada).
  • A validação de consultas SQL em tempo de compilação é a principal vantagem, eliminando erros de execução nas consultas.
  • O suporte integrado para Flow, LiveData e RxJava permite construir arquiteturas reativas com atualizações automáticas da interface do usuário quando os dados mudam.
  • As migrações no Room garantem atualizações perfeitas do esquema do banco de dados sem perder informações salvas do usuário.
  • A biblioteca se integra com corrotinas Kotlin através de funções suspend, simplificando o trabalho assíncrono com dados.
  • Para novos projetos, Room é a solução oficialmente recomendada pelo Google para armazenamento local de dados no Android.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também