Room: qué es, biblioteca ORM y trabajo con SQLite

Autor: IT Sectr Publicado: 2026-03-12 Tiempo de lectura: 10 min

Room es una biblioteca ORM de Android Jetpack que proporciona una capa de abstracción sobre SQLite para trabajar con bases de datos locales en Android. Según la documentación oficial en Android Developers, 2025, Room genera automáticamente implementaciones de DAO basadas en anotaciones en tiempo de compilación, eliminando alrededor del 70% del código repetitivo en comparación con el uso directo de SQLiteOpenHelper. La biblioteca realiza la validación de consultas SQL en tiempo de compilación, lo que permite detectar errores de sintaxis antes de ejecutar la aplicación en un dispositivo.

Puntos Clave

  • Room es una biblioteca ORM de Android Jetpack que proporciona una capa de abstracción sobre SQLite para el almacenamiento local de datos en aplicaciones Android.
  • Tres componentes principales: Entity (definición de tabla), DAO (operaciones con datos) y Database (punto de entrada a la base de datos).
  • La validación de consultas SQL en tiempo de compilación es una ventaja clave, permitiendo encontrar errores antes de instalar la aplicación.
  • Soporte integrado para Flow, LiveData y RxJava para la observación reactiva de cambios en la base de datos.
  • El mecanismo de migraciones permite actualizar el esquema de la base de datos sin perder los datos de usuario ya guardados.

¿Qué es la biblioteca Room ORM?

Room es una biblioteca ORM de Android Jetpack creada por Google para simplificar el trabajo con bases de datos SQLite locales en la plataforma Android. Proporciona anotaciones para describir el esquema de datos y genera automáticamente implementaciones de interfaces DAO en tiempo de compilación. A diferencia del uso directo de SQLiteOpenHelper, Room libera al desarrollador de escribir una cantidad significativa de código repetitivo para crear, abrir y gestionar conexiones con la base de datos.

La biblioteca fue presentada en Google I/O 2017 como parte de los componentes de arquitectura de Android. Desde entonces, Room se ha convertido en el estándar de facto para el almacenamiento local de datos, superando en popularidad a soluciones como GreenDAO y Realm para Android. Según Google, la biblioteca se utiliza en más del 60% de las aplicaciones publicadas en Google Play que trabajan con datos locales en el dispositivo.

La característica clave es la validación de consultas SQL en tiempo de compilación mediante un procesador de anotaciones. Si un desarrollador comete un error en un comando SQL, por ejemplo, especificando un nombre de columna inexistente, la compilación fallará con un error antes de que la aplicación se instale. Esto es fundamentalmente diferente del enfoque de SQLiteOpenHelper, donde dichos errores solo se detectan en tiempo de ejecución, a menudo en producción.

TypeConverters para tipos no estándar

SQLite solo admite cinco tipos de datos: TEXT, INTEGER, REAL, BLOB y NULL. Sin embargo, Java y Kotlin utilizan tipos complejos: Date, List, Enum y objetos personalizados. Para almacenarlos, Room proporciona el mecanismo TypeConverters: métodos estáticos que convierten un tipo complejo en un tipo primitivo comprensible para SQLite. Por ejemplo, un objeto Date se convierte a Long (timestamp), y List<String> a una cadena JSON mediante Gson o 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 un convertidor, basta con añadir la anotación @TypeConverter a un método estático y especificar la clase convertidora en la anotación @TypeConverters a nivel de base de datos. Room aplica automáticamente el convertidor al leer y escribir el tipo correspondiente en cada consulta SQL sin necesidad de llamar manualmente a los métodos de conversión.

Arquitectura de Room: tres componentes clave

Room consta de tres componentes principales: Entity, DAO y Database. Cada uno cumple un rol estrictamente definido y se anota con la anotación correspondiente. Juntos forman una capa completa de acceso a datos que aísla la lógica de negocio de la aplicación de los detalles de implementación de SQLite.

Entity — Tabla de base de datos

Entity es una clase de datos que describe la estructura de una tabla en la base de datos. Cada campo de la clase corresponde a una columna de la tabla, y cada fila en la base de datos corresponde a una instancia de la clase. La anotación @Entity indica a Room que la clase es una tabla. El campo con la anotación @PrimaryKey define la clave primaria, que puede ser autoincremental o compuesta. @ForeignKey se utiliza para las relaciones entre tablas, garantizando la integridad de los datos a nivel de base de datos.

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 — Operaciones con datos

DAO (Data Access Object) es una interfaz o clase abstracta que declara operaciones para trabajar con datos: insertar, leer, actualizar y eliminar. Cada operación se anota con @Insert, @Query, @Update o @Delete. Room genera automáticamente la implementación de esta interfaz en tiempo de compilación. La anotación @Query es particularmente valiosa: acepta una consulta SQL como cadena y valida su corrección en tiempo de compilación.

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 — Punto de entrada

Database es una clase abstracta que extiende RoomDatabase y sirve como punto de entrada a la base de datos. Contiene una lista de todas las Entities y proporciona métodos abstractos para obtener DAOs. La clase se anota con @Database, que especifica la versión del esquema y la lista de entidades. La instancia de la base de datos se crea mediante Room.databaseBuilder con el contexto de la aplicación, el nombre del archivo y la clase Database.

Cómo funciona Room con SQLite bajo el capó

Room no reemplaza SQLite, sino que funciona sobre él como una capa de abstracción. La arquitectura interna incluye un procesador de anotaciones, un generador de código y un grupo de conexiones. En tiempo de compilación, el procesador de anotaciones analiza las clases Entity, DAO y Database, y luego genera clases de implementación con el sufijo _Impl. Todas las clases generadas se colocan en el paquete de compilación y no son visibles directamente para el desarrollador.

La generación de código en tiempo de compilación es el mecanismo central de Room. Para cada interfaz DAO, se genera una clase con la implementación completa de todos los métodos anotados. Las consultas SQL de la anotación @Query se validan para verificar su corrección: el procesador compara los nombres de las columnas con los campos de Entity y verifica la sintaxis SQL. Si se encuentra un error, la compilación se interrumpe con un mensaje claro. Esto es imposible cuando se usa SQLiteOpenHelper directamente, donde los errores solo aparecen en tiempo de ejecución.

Generación de código en tiempo de compilación

El proceso de generación incluye tres etapas. Primera — validación del esquema: el procesador verifica que todas las clases listadas en @Database sean Entities válidas. Segunda — generación del cuerpo del DAO: para cada método se crea una implementación utilizando el objeto interno RoomSQLiteQuery que ejecuta consultas preparadas. Tercera — generación de la clase Database_Impl, que maneja la creación y apertura de la base de datos, así como la inicialización de todos los 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)
        }
    }
}

Room no crea un grupo de hilos separado para las operaciones de base de datos. Por defecto, las consultas se ejecutan en el hilo que las llama con una limitación: la lectura y escritura bloquean el hilo. Para el trabajo asíncrono, Room se integra con las corutinas de Kotlin mediante funciones suspend, con LiveData a través de valores de retorno, y con Flow mediante envoltorios reactivos. Esto le da al desarrollador la flexibilidad de elegir la solución arquitectónica para una tarea específica.

Ejemplo de uso de Room en una aplicación Android

Veamos un ejemplo práctico de creación de una aplicación para tomar notas usando Room. La aplicación contiene una tabla Note con los campos id, title, content y timestamp. Los usuarios pueden añadir, ver y eliminar notas. Se utilizan corutinas para las operaciones asíncronas.

Configuración de dependencias Gradle

Para integrar Room en un proyecto Android, añada dependencias al archivo build.gradle del módulo de la aplicación. Room requiere tres componentes: la biblioteca de tiempo de ejecución, el procesador de anotaciones kapt y el soporte opcional para corutinas. La versión de la biblioteca se especifica en una variable room_version para facilitar la actualización. A partir de Room 2.4.0, se admite KSP como alternativa a kapt con velocidades de compilación más 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: pruebas
    testImplementation "androidx.room:room-testing:$room_version"
}

Después de configurar las dependencias, cree tres archivos: la Entity Note, la interfaz NoteDao y la clase AppDatabase. La Entity Note contiene campos con anotaciones @PrimaryKey y @ColumnInfo. El DAO proporciona métodos para insertar, obtener la lista y eliminar. La Database vincula la Entity y el DAO mediante la anotación @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)
}

El archivo AppDatabase se declara como una clase abstracta que extiende RoomDatabase. La anotación @Database especifica todas las Entities para la versión actual y el número de versión del esquema. Para obtener una instancia, se utiliza el patrón singleton mediante el método build de Room.databaseBuilder con el contexto de la aplicación. El almacenamiento en caché de la instancia de la base de datos evita múltiples creaciones que podrían provocar fugas de memoria.

Migraciones de base de datos en Room

Las Migraciones en Room son un mecanismo para cambiar el esquema de la base de datos al actualizar una aplicación sin perder los datos existentes. Cuando un usuario instala una nueva versión con Entities modificadas, Room detecta la discrepancia de versiones y ejecuta los pasos de migración especificados. Sin una migración, la base de datos se eliminará y se recreará, lo que resultará en la pérdida de todos los datos guardados por el usuario.

Una migración se describe mediante la clase Migration, que toma las versiones inicial y final de la base de datos. Dentro del método migrate, se ejecuta una consulta SQL ALTER TABLE o CREATE TABLE para cambiar el esquema. Room no puede detectar automáticamente los cambios de esquema — el desarrollador debe escribir una migración manualmente para cada cambio de Entity. A partir de Room 2.4.0, la función experimental autoMigrations está disponible para la generación automática de migraciones.

Migraciones automáticas con autoMigrations

La función autoMigrations permite a Room generar migraciones automáticamente basándose en las diferencias entre versiones de Entity. Para usarla, basta con añadir la anotación @AutoMigration a @Database y habilitar la exportación del esquema a JSON. Room compara los esquemas de versiones adyacentes y genera las consultas ALTER necesarias. Sin embargo, autoMigrations solo admite cambios compatibles hacia atrás: añadir columnas, crear índices y cambiar tipos con transformaciones compatibles.

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 cambios complejos, como renombrar columnas o fusionar tablas, se requiere una migración manual utilizando tablas intermedias. Un escenario típico: crear una tabla temporal con el esquema antiguo, copiar datos de la tabla antigua a la nueva con transformaciones, eliminar la tabla antigua y renombrar la temporal. Room garantiza que todas las migraciones se ejecuten en una sola transacción, y si ocurre un error, los cambios se revierten por completo.

Preguntas Frecuentes

¿En qué se diferencia Room de SQLiteOpenHelper?

Room proporciona una abstracción ORM con anotaciones y validación de SQL en tiempo de compilación, mientras que SQLiteOpenHelper requiere escribir manualmente todas las consultas y gestionar las conexiones. Room genera automáticamente código para operaciones CRUD y se integra con los componentes de arquitectura de Android, incluyendo LiveData y Flow.

¿Qué tipos de datos soporta Room?

Room soporta todos los tipos primitivos de Java: Int, Long, Boolean, Float, Double, así como String, ByteArray y Date. Para tipos complejos como List o Enum, se utilizan TypeConverters — métodos de conversión estáticos que convierten tipos no estándar en formatos compatibles con SQLite.

¿Se puede usar Room sin corutinas?

Sí, Room admite llamadas síncronas sin corutinas, pero bloquean el hilo en el que se ejecutan. Para trabajo asíncrono, puede usar LiveData o RxJava en lugar de corutinas. Google recomienda usar corutinas como método principal para el acceso asíncrono a datos en nuevos proyectos.

¿Qué ocurre si falta una migración?

Si Room detecta una discrepancia en la versión de la base de datos y no encuentra una migración adecuada, por defecto lanza una IllegalStateException con una descripción del error. El desarrollador puede anular este comportamiento con el método fallbackToDestructiveMigration, que eliminará la base de datos existente y creará una nueva, perdiendo todos los datos.

¿Cómo maneja Room las relaciones entre tablas?

Room soporta relaciones mediante objetos anidados con la anotación @Embedded y mediante clases de relación con la anotación @Relation. Para consultas complejas que implican uniones de tablas, se utilizan clases POJO personalizadas, cuyos campos se rellenan a partir de los resultados de @Query con sentencias SQL JOIN.

Resumen

  • Room es una biblioteca ORM de Android Jetpack que crea una capa de abstracción sobre SQLite para el almacenamiento conveniente de datos en el dispositivo.
  • La arquitectura se basa en tres componentes: Entity (esquema de tabla), DAO (operaciones) y Database (punto de entrada).
  • La validación de consultas SQL en tiempo de compilación es la principal ventaja, eliminando errores de ejecución en las consultas.
  • El soporte integrado para Flow, LiveData y RxJava permite construir arquitecturas reactivas con actualizaciones automáticas de la interfaz de usuario cuando los datos cambian.
  • Las migraciones en Room garantizan actualizaciones sin problemas del esquema de la base de datos sin perder la información guardada del usuario.
  • La biblioteca se integra con las corutinas de Kotlin mediante funciones suspend, simplificando el trabajo asíncrono con datos.
  • Para nuevos proyectos, Room es la solución oficialmente recomendada por Google para el almacenamiento local de datos en Android.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también