Integración de Room como Base de Datos en Android
Clase 6 de 19 • Curso de Android: Modo Offline con Room y Realm
Resumen
La integración de Room como base de datos local en aplicaciones Android es fundamental para gestionar datos de manera eficiente cuando no hay conexión a internet o para mejorar el rendimiento de la aplicación. Room proporciona una capa de abstracción sobre SQLite que facilita el acceso a la base de datos mientras aprovecha toda su potencia.
¿Cómo configurar Room en un proyecto Android?
Para comenzar a trabajar con Room, es necesario configurar correctamente las dependencias en nuestro proyecto. Esto implica añadir las bibliotecas necesarias al archivo build.gradle del módulo app:
// Dependencias de Room
implementation "androidx.room:room-runtime:2.6.1"
implementation "androidx.room:room-ktx:2.6.1"
kapt "androidx.room:room-compiler:2.6.1"
Es importante organizar adecuadamente la estructura del proyecto para mantener una clara separación entre las fuentes de datos remotas y locales. La arquitectura recomendada divide la capa de datos en dos paquetes principales: "remote" para las conexiones a servicios externos y "local" para la gestión de datos con Room.
¿Qué son las entidades en Room y cómo se implementan?
Las entidades en Room representan las tablas en la base de datos SQLite. Cada entidad se define como una clase de datos (data class) con anotaciones específicas:
@Entity(tableName = "orders")
data class OrderEntity(
@PrimaryKey(autoGenerate = false)
val id: String,
val customerName: String,
val item: String,
val total: Double,
val meshImageUrl: String
)
En este ejemplo, creamos una entidad llamada OrderEntity
que representa una tabla de pedidos. La anotación @Entity
indica a Room que esta clase debe ser tratada como una tabla en la base de datos, mientras que @PrimaryKey
define la clave primaria.
Para casos donde necesitamos almacenar datos temporalmente antes de sincronizarlos con un servidor, podemos crear entidades adicionales:
@Entity(tableName = "pre_orders")
data class PreOrderEntity(
@PrimaryKey(autoGenerate = true)
val id: Long,
val item: String,
val customerName: String,
val isSend: Boolean
)
En esta entidad PreOrderEntity
, el campo isSend
actúa como un indicador que nos permite saber si el registro ha sido sincronizado con el servidor remoto o solo existe localmente.
¿Cómo implementar los DAOs para acceder a los datos?
Los DAOs (Data Access Objects) son interfaces que contienen los métodos para interactuar con la base de datos. Room utiliza estas interfaces para generar el código necesario para realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar):
@Dao
interface PreOrderDao {
@Insert
suspend fun savePreOrder(preOrderEntity: PreOrderEntity)
@Query("SELECT * FROM pre_orders")
fun getPreOrders(): Flow<List<PreOrderEntity>>
@Query("DELETE FROM pre_orders WHERE id = :id")
suspend fun deletePreOrder(id: Long)
@Query("UPDATE pre_orders SET isSend = 1 WHERE id = :id")
suspend fun updatePreOrderStatus(id: Long)
}
En este DAO para las pre-órdenes, definimos métodos para:
- Guardar una nueva pre-orden
- Obtener todas las pre-órdenes como un Flow (para observar cambios)
- Eliminar una pre-orden específica
- Actualizar el estado de sincronización
Para las órdenes regulares, podemos implementar otro DAO:
@Dao
interface OrderDao {
@Upsert
suspend fun insertOrders(orders: List<OrderEntity>)
@Query("SELECT * FROM orders")
fun getOrders(): Flow<List<OrderEntity>>
@Query("SELECT * FROM orders WHERE id = :id")
suspend fun getOrder(id: String): OrderEntity
}
La anotación @Upsert
es una característica más reciente de Room que combina las operaciones de inserción y actualización. Si el registro ya existe, lo reemplaza; si no existe, lo inserta.
¿Cómo configurar la base de datos y la inyección de dependencias?
Para completar la configuración de Room, necesitamos definir una clase abstracta que extienda RoomDatabase
:
@Database(entities = [OrderEntity::class, PreOrderEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun orderDao(): OrderDao
abstract fun preOrderDao(): PreOrderDao
}
Esta clase define la estructura de la base de datos, incluyendo las entidades que contiene y su versión. Los métodos abstractos proporcionan acceso a los DAOs que hemos creado.
Finalmente, para integrar Room con Hilt (un framework de inyección de dependencias), creamos un módulo:
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Provides
@Singleton
fun provideDatabase(application: Application): AppDatabase {
return Room.databaseBuilder(
application,
AppDatabase::class.java,
"run_database"
).build()
}
@Provides
fun provideOrderDao(database: AppDatabase): OrderDao {
return database.orderDao()
}
@Provides
fun providePreOrderDao(database: AppDatabase): PreOrderDao {
return database.preOrderDao()
}
}
Este módulo proporciona las instancias necesarias de la base de datos y los DAOs para que puedan ser inyectados en otras partes de la aplicación.
¿Cuáles son las ventajas de usar Room?
Room ofrece numerosas ventajas sobre el uso directo de SQLite:
- Verificación en tiempo de compilación de consultas SQL
- Conversión automática entre objetos Java/Kotlin y entidades de base de datos
- Soporte para tipos de retorno reactivos como Flow
- Integración con corrutinas para operaciones asíncronas
- Migraciones de esquema simplificadas
La implementación de Room como base de datos local es un paso fundamental para crear aplicaciones Android robustas que funcionen bien incluso sin conexión a internet. La combinación de Room con patrones de arquitectura modernos como MVVM y herramientas como Hilt proporciona una base sólida para el desarrollo de aplicaciones escalables y mantenibles.
¿Has implementado Room en tus proyectos Android? Comparte tus experiencias y dudas en los comentarios para seguir aprendiendo juntos sobre el desarrollo de aplicaciones con almacenamiento local eficiente.