Capitulo 4 del Módulo 2 Navegación y Base de Datos

➜ Base de Datos Room

Room en Kotlin - CRUD y DAOs | Jetpack Compose App Carta Digital | Implementa Room en tu app con Kotlin y Jetpack Compose. Crea entidades, DAOs, relaciones y CRUD para tu carta digital. Ideal para apps offline.

En este capítulo implementaremos una base de datos local utilizando Room, la librería oficial de persistencia para Android. Con esto podrás almacenar de forma estructurada las categorías, productos y pedidos de tu app, incluso sin conexión a internet.


Configura las dependencias de Room

Abre el archivo build.gradle.kts (o build.gradle) y agrega las siguientes dependencias:

// BASE DE DATOS ROOM
implementation(libs.androidx.room.runtime)
kapt(libs.androidx.room.compiler)
implementation(libs.androidx.room.ktx)

Asegúrate de tener configurado kapt en tu proyecto para usar Room con anotaciones.

 

Luego en libs.versions.toml

[versions]

room = "2.7.1"

 

[libraries]

androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }

 


Crea las entidades

Las entidades representan las tablas de la base de datos. Vamos a crear tres: Category, Item y Order.

🗂️ Category.kt

@Entity(tableName = "categories")
data class Category(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val name: String
)

🍽️ Item.kt

@Entity(
    tableName = "items",
    foreignKeys = [ForeignKey(
        entity = Category::class,
        parentColumns = ["id"],
        childColumns = ["categoryId"],
        onDelete = ForeignKey.CASCADE
    )]
)
data class Item(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val name: String,
    val description: String?,
    val price: Double,
    val imageUrl: String?,
    val categoryId: Int
)

🧾 Order.kt

@Entity(
    tableName = "orders",
    foreignKeys = [ForeignKey(
        entity = Item::class,
        parentColumns = ["id"],
        childColumns = ["itemId"],
        onDelete = ForeignKey.CASCADE
    )]
)
data class Order(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val itemId: Int,
    val quantity: Int,
    val itemPrice: Double
)

Crea los DAO (Data Access Object)

Los DAO definen las operaciones (CRUD) para cada entidad.

📂 CategoryDao.kt

@Dao
interface CategoryDao {

    @Insert
    suspend fun insert(category: Category)

    @Query("SELECT * FROM categories ORDER BY id ASC")
    fun getAllCategories(): Flow<List<Category>>

    @Query("SELECT * FROM categories WHERE id = :id")
    fun getCategoryById(id: Int): Category?

    @Update
    suspend fun update(category: Category)

    @Delete
    suspend fun delete(category: Category)
}

🧾 ItemDao.kt

@Dao
interface ItemDao {

    @Insert
    suspend fun insert(item: Item)

    @Query("SELECT * FROM items ORDER BY id ASC")
    fun getAllItems(): Flow<List<Item>>

    @Query("SELECT * FROM items WHERE categoryId = :categoryId ORDER BY id ASC")
    fun getItemsForCategory(categoryId: Int): Flow<List<Item>>

    @Query("SELECT * FROM items WHERE id = :id")
    fun getItemsById(id: Int): Item?

    @Update
    suspend fun update(item: Item)

    @Delete
    suspend fun delete(item: Item)
}

🛒 OrderDao.kt

@Dao
interface OrderDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(order: Order)

    @Update
    suspend fun update(order: Order)

    @Delete
    suspend fun delete(order: Order)

    @Query("DELETE FROM orders")
    suspend fun clearCart()

    @Query("""
        SELECT orders.*, items.name AS itemName, items.price AS itemOriginalPrice, items.imageUrl AS itemImageUrl
        FROM orders JOIN items ON orders.itemId = items.id
        ORDER BY itemName ASC
    """)
    fun getAllOrdersWithItemDetails(): Flow<List<OrderWithItem>>

    data class OrderWithItem(
        val id: Int,
        val itemId: Int,
        val quantity: Int,
        val itemPrice: Double,
        val itemName: String,
        val itemOriginalPrice: Double,
        val itemImageUrl: String?
    )
}

✅ Nota: Corregimos el JOIN en la consulta para relacionar orders.itemId con items.id.


Implementa AppDatabase.kt

Esta clase conecta todas las entidades y DAOs:

@Database(
    entities = [Category::class, Item::class, Order::class],
    version = 1
)
abstract class AppDataBase : RoomDatabase() {

    abstract fun CategoryDao(): CategoryDao
    abstract fun ItemDao(): ItemDao
    abstract fun OrderDao(): OrderDao

    companion object {
        @Volatile
        private var INSTANCE: AppDataBase? = null

        fun getDataBase(context: Context): AppDataBase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDataBase::class.java,
                    "carta_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

Conclusión

Con esto ya tienes toda la infraestructura de base de datos lista para funcionar en tu app:

  • Tablas con relaciones bien definidas
  • DAOs con funciones CRUD
  • Base de datos centralizada

519 visitas

Capítulo 5 – ViewModels »

Descarga el código del proyecto

Descarga el código fuente del proyecto adquiriendo el curso completo

Comprar

¡Qué aprenderás?

tooltip bs-tooltip-top bs-tooltip-end bs-tooltip-bottom bs-tooltip-start show fade tooltip-inner

Codea Applications

México, Colombia, España, Venezuela, Argentina, Bolivia, Perú