Skip to content

数据库(Room)使用说明

目录:app/src/main/java/com/joker/kit/core/database
配套示例:feature/demo/viewmodel/DatabaseViewModel.ktfeature/demo/view/DatabaseScreen.kt

快速认知

  • 架构链路:Entity → DAO → DataSource → Repository → ViewModel → UI
  • 依赖:gradle/libs.versions.toml 中的 room-* 版本号
  • 当前内置 Demo 表,但骨架已覆盖生产所需流程(CRUD、Flow、DI、示例页面)

目录速览

路径说明
AppDatabase.ktRoom 入口,集中注册实体与 DAO。
entity/实体定义(示例 DemoEntity)。
dao/DAO 接口与 SQL(示例 DemoDao)。
datasource/封装 DAO,补充业务字段/线程(示例 DemoDataSource)。
di/DatabaseModule.ktHilt 提供 AppDatabase、DAO。

手把手:新增一张表(可直接照抄路径)

假设要新增购物车表 Cart,按顺序完成下列文件:

  1. 定义实体 app/src/main/java/com/joker/kit/core/database/entity/CartEntity.kt
kotlin
@Entity(tableName = "cart_items")
data class CartEntity(
    @PrimaryKey(autoGenerate = true) val id: Long = 0,          // 自增主键
    val goodsId: Long,                                          // 商品 ID
    val count: Int,                                             // 购买数量
    val updatedAt: Long = System.currentTimeMillis()            // 更新时间戳
)
  1. 编写 DAO app/src/main/java/com/joker/kit/core/database/dao/CartDao.kt
kotlin
@Dao
interface CartDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun upsert(item: CartEntity)                        // 插入或更新

    @Query("SELECT * FROM cart_items ORDER BY updatedAt DESC")
    fun observeCart(): Flow<List<CartEntity>>                   // 订阅列表

    @Query("DELETE FROM cart_items WHERE id = :id")
    suspend fun deleteById(id: Long)                            // 删除单条
}
  1. 注册数据库 AppDatabase.kt
kotlin
@Database(
    entities = [DemoEntity::class, CartEntity::class], // 在这里追加新表
    version = 1,
    exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun demoDao(): DemoDao
    abstract fun cartDao(): CartDao              // 新增 DAO getter
}
  1. 注入 DAO di/DatabaseModule.kt
kotlin
@Provides @Singleton
fun provideCartDao(db: AppDatabase): CartDao = db.cartDao()     // Hilt 提供 CartDao
  1. 封装 DataSource app/src/main/java/com/joker/kit/core/database/datasource/CartDataSource.kt
kotlin
@Singleton
class CartDataSource @Inject constructor(
    private val cartDao: CartDao
) {
    fun observeCart(): Flow<List<CartEntity>> = cartDao.observeCart()  // Flow 供 UI 订阅

    suspend fun addOrUpdate(item: CartEntity) = cartDao.upsert(item.copy(
        updatedAt = System.currentTimeMillis()                          // 自动刷新时间
    ))
    suspend fun remove(id: Long) = cartDao.deleteById(id)              // 删除单条
}
  1. 仓库层暴露接口(保持与 Demo 一致的层次) app/src/main/java/com/joker/kit/core/data/repository/CartRepository.kt
kotlin
class CartRepository @Inject constructor(
    private val cartDataSource: CartDataSource
) {
    fun observeCart(): Flow<List<CartEntity>> = cartDataSource.observeCart()   // 直接转发

    suspend fun addItem(goodsId: Long, count: Int) =
        cartDataSource.addOrUpdate(
            CartEntity(goodsId = goodsId, count = count)                       // 业务入参转实体
        )

    suspend fun removeItem(id: Long) = cartDataSource.remove(id)
}
  1. ViewModel / UI 接入(参照 Demo 示例)
  • ViewModel 直接注入 CartRepository,把 observeCart() 转成 StateFlow,暴露 addItem/removeItem 方法。
  • Compose 页面 collectAsState() 订阅列表,按钮调用仓库方法。

至此,新表从实体到 UI 的全链路完成,阅读以上片段即可落地,无需翻源代码。

现有 Demo:可运行的增删改查样例

  • ViewModelfeature/demo/viewmodel/DatabaseViewModel.kt
    • StateFlow 持有输入与列表;封装新增/删除/清空。
  • UIfeature/demo/view/DatabaseScreen.kt
    • 文本框 + 列表 + 删除/清空按钮,直接 collect 数据。 打开 App → “数据库”菜单即可体验。

设计要点

  • 单向数据流:Room 只到 Repository,不直接给 UI;便于后续接入网络缓存/业务校验。
  • 协程与线程:DAO 调用放在 Repository/DataSource;UI 只收集 Flow 或调用挂起函数。
  • 迁移提醒:新增字段/表需提升 version 并提供 Migration;开发期可用 fallbackToDestructiveMigration(),生产环境请谨慎。
  • 测试:用 Room.inMemoryDatabaseBuilder 构建内存库,便于单元测试。

代码速查(现有 Demo 片段)

  • DataSource 自动更新时间
kotlin
class DemoDataSource @Inject constructor(private val demoDao: DemoDao) {
    suspend fun createItem(title: String, desc: String = "") =
        demoDao.insertItem(DemoEntity(title = title, description = desc))

    suspend fun updateItem(item: DemoEntity) =
        demoDao.updateItem(item.copy(updatedAt = System.currentTimeMillis()))

    fun observeItems(): Flow<List<DemoEntity>> = demoDao.getAllItems()
}
  • ViewModel 转 StateFlow
kotlin
val items: StateFlow<List<DemoEntity>> = demoRepository.observeItems()
    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())

用以上“新增表 5+2 步”作为模板,即可在不翻源代码的情况下完成新表落地。***