数据库(Room)使用说明
目录:
app/src/main/java/com/joker/kit/core/database
配套示例:feature/demo/viewmodel/DatabaseViewModel.kt、feature/demo/view/DatabaseScreen.kt
快速认知
- 架构链路:Entity → DAO → DataSource → Repository → ViewModel → UI
- 依赖:
gradle/libs.versions.toml中的room-*版本号 - 当前内置 Demo 表,但骨架已覆盖生产所需流程(CRUD、Flow、DI、示例页面)
目录速览
| 路径 | 说明 |
|---|---|
AppDatabase.kt | Room 入口,集中注册实体与 DAO。 |
entity/ | 实体定义(示例 DemoEntity)。 |
dao/ | DAO 接口与 SQL(示例 DemoDao)。 |
datasource/ | 封装 DAO,补充业务字段/线程(示例 DemoDataSource)。 |
di/DatabaseModule.kt | Hilt 提供 AppDatabase、DAO。 |
手把手:新增一张表(可直接照抄路径)
假设要新增购物车表 Cart,按顺序完成下列文件:
- 定义实体
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() // 更新时间戳
)- 编写 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) // 删除单条
}- 注册数据库
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
}- 注入 DAO
di/DatabaseModule.kt
kotlin
@Provides @Singleton
fun provideCartDao(db: AppDatabase): CartDao = db.cartDao() // Hilt 提供 CartDao- 封装 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) // 删除单条
}- 仓库层暴露接口(保持与 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)
}- ViewModel / UI 接入(参照 Demo 示例)
- ViewModel 直接注入
CartRepository,把observeCart()转成StateFlow,暴露addItem/removeItem方法。 - Compose 页面
collectAsState()订阅列表,按钮调用仓库方法。
至此,新表从实体到 UI 的全链路完成,阅读以上片段即可落地,无需翻源代码。
现有 Demo:可运行的增删改查样例
- ViewModel:
feature/demo/viewmodel/DatabaseViewModel.kt- StateFlow 持有输入与列表;封装新增/删除/清空。
- UI:
feature/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 步”作为模板,即可在不翻源代码的情况下完成新表落地。***