Room数据库 #

一、Room概述 #

Room是Jetpack提供的SQLite抽象层,提供了编译时SQL验证、便捷的注解支持,使数据库操作更加简单和安全。

1.1 Room的优势 #

  • 编译时SQL验证
  • 便捷的注解支持
  • 支持LiveData/Flow
  • 支持协程
  • 支持RxJava

1.2 添加依赖 #

kotlin
dependencies {
    val roomVersion = "2.6.1"
    implementation("androidx.room:room-runtime:$roomVersion")
    implementation("androidx.room:room-ktx:$roomVersion")
    kapt("androidx.room:room-compiler:$roomVersion")
    
    // 或使用KSP
    // ksp("androidx.room:room-compiler:$roomVersion")
}

1.3 Room架构 #

text
┌─────────────────────────────────────────┐
│              Database                    │
│    (RoomDatabase的子类)                  │
├─────────────────────────────────────────┤
│              DAO                         │
│    (数据访问对象接口)                     │
├─────────────────────────────────────────┤
│              Entity                      │
│    (实体类,对应数据库表)                 │
└─────────────────────────────────────────┘

二、Entity实体类 #

2.1 基本实体 #

kotlin
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    
    @ColumnInfo(name = "name")
    val name: String,
    
    @ColumnInfo(name = "email")
    val email: String,
    
    @ColumnInfo(name = "age")
    val age: Int
)

2.2 主键 #

kotlin
// 自增主键
@PrimaryKey(autoGenerate = true)
val id: Long = 0

// 复合主键
@Entity(primaryKeys = ["firstName", "lastName"])
data class User(
    val firstName: String,
    val lastName: String
)

2.3 忽略字段 #

kotlin
@Entity
data class User(
    @PrimaryKey val id: Long,
    val name: String,
    
    @Ignore
    val tempData: String  // 不存储到数据库
)

2.4 嵌入对象 #

kotlin
data class Address(
    val street: String,
    val city: String,
    val zipCode: String
)

@Entity
data class User(
    @PrimaryKey val id: Long,
    val name: String,
    
    @Embedded
    val address: Address
)

2.5 索引 #

kotlin
@Entity(
    tableName = "users",
    indices = [
        Index(value = ["name"]),
        Index(value = ["email"], unique = true)
    ]
)
data class User(
    @PrimaryKey val id: Long,
    val name: String,
    val email: String
)

2.6 外键 #

kotlin
@Entity(
    tableName = "orders",
    foreignKeys = [
        ForeignKey(
            entity = User::class,
            parentColumns = ["id"],
            childColumns = ["userId"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class Order(
    @PrimaryKey val id: Long,
    val userId: Long,
    val amount: Double
)

三、DAO数据访问对象 #

3.1 插入 #

kotlin
@Dao
interface UserDao {
    
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(user: User): Long
    
    @Insert
    suspend fun insertAll(users: List<User>)
}

3.2 更新 #

kotlin
@Dao
interface UserDao {
    
    @Update
    suspend fun update(user: User): Int
    
    @Query("UPDATE users SET name = :name WHERE id = :id")
    suspend fun updateName(id: Long, name: Int): Int
}

3.3 删除 #

kotlin
@Dao
interface UserDao {
    
    @Delete
    suspend fun delete(user: User): Int
    
    @Query("DELETE FROM users WHERE id = :id")
    suspend fun deleteById(id: Long): Int
    
    @Query("DELETE FROM users")
    suspend fun deleteAll()
}

3.4 查询 #

kotlin
@Dao
interface UserDao {
    
    @Query("SELECT * FROM users")
    suspend fun getAll(): List<User>
    
    @Query("SELECT * FROM users WHERE id = :id")
    suspend fun getById(id: Long): User?
    
    @Query("SELECT * FROM users WHERE name LIKE :keyword")
    suspend fun searchByName(keyword: String): List<User>
    
    @Query("SELECT * FROM users WHERE age BETWEEN :minAge AND :maxAge")
    suspend fun getByAgeRange(minAge: Int, maxAge: Int): List<User>
    
    @Query("SELECT * FROM users ORDER BY name ASC")
    suspend fun getAllOrderedByName(): List<User>
    
    // 返回Flow
    @Query("SELECT * FROM users")
    fun getAllFlow(): Flow<List<User>>
    
    // 返回LiveData
    @Query("SELECT * FROM users")
    fun getAllLiveData(): LiveData<List<User>>
    
    // 返回PagingSource
    @Query("SELECT * FROM users")
    fun getPagingSource(): PagingSource<Int, User>
}

3.5 关系查询 #

kotlin
data class UserWithOrders(
    @Embedded val user: User,
    @Relation(
        parentColumn = "id",
        entityColumn = "userId"
    )
    val orders: List<Order>
)

@Dao
interface UserDao {
    @Transaction
    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserWithOrders(userId: Long): UserWithOrders
}

四、Database数据库 #

4.1 创建Database #

kotlin
@Database(
    entities = [User::class, Order::class],
    version = 1,
    exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    abstract fun orderDao(): OrderDao
}

4.2 获取Database实例 #

kotlin
class MyApp : Application() {
    
    companion object {
        private var database: AppDatabase? = null
        
        fun getDatabase(context: Context): AppDatabase {
            return database ?: synchronized(this) {
                database ?: Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database"
                ).build().also { database = it }
            }
        }
    }
}

4.3 在Activity中使用 #

kotlin
class MainActivity : AppCompatActivity() {
    
    private val database by lazy { MyApp.getDatabase(this) }
    private val userDao by lazy { database.userDao() }
    
    private lateinit var binding: ActivityMainBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        lifecycleScope.launch {
            val users = userDao.getAll()
            updateUI(users)
        }
        
        // 观察数据变化
            .onEach { users ->
                updateUI(users)
            }
            .launchIn(lifecycleScope)
    }
}

五、数据库迁移 #

5.1 定义迁移 #

kotlin
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE users ADD COLUMN phone TEXT")
    }
}

val MIGRATION_2_3 = object : Migration(2, 3) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("CREATE TABLE orders (id INTEGER PRIMARY KEY, userId INTEGER, amount REAL)")
    }
}

5.2 添加迁移 #

kotlin
Room.databaseBuilder(
    context.applicationContext,
    AppDatabase::class.java,
    "app_database"
)
    .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
    .build()

5.3 处理迁移失败 #

kotlin
Room.databaseBuilder(
    context.applicationContext,
    AppDatabase::class.java,
    "app_database"
)
    .addMigrations(MIGRATION_1_2)
    .fallbackToDestructiveMigration()  // 迁移失败时重建数据库
    .build()

5.4 数据库版本更新 #

kotlin
@Database(
    entities = [User::class, Order::class],
    version = 3,
    exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    abstract fun orderDao(): OrderDao
}

六、TypeConverter #

6.1 定义转换器 #

kotlin
class Converters {
    
    @TypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return value?.let { Date(it) }
    }
    
    @TypeConverter
    fun dateToTimestamp(date: Date?): Long? {
        return date?.time
    }
    
    @TypeConverter
    fun fromStringList(value: String?): List<String> {
        return value?.split(",") ?: emptyList()
    }
    
    @TypeConverter
    fun stringListToString(list: List<String>?): String? {
        return list?.joinToString(",")
    }
}

6.2 注册转换器 #

kotlin
@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

七、最佳实践 #

7.1 使用Repository模式 #

kotlin
class UserRepository(private val userDao: UserDao) {
    
    fun getAllFlow(): Flow<List<User>> = userDao.getAllFlow()
    
    suspend fun insert(user: User): Long = userDao.insert(user)
    
    suspend fun update(user: User): Int = userDao.update(user)
    
    suspend fun delete(user: User): Int = userDao.delete(user)
    
    suspend fun getById(id: Long): User? = userDao.getById(id)
}

7.2 使用ViewModel #

kotlin
class UserViewModel(private val repository: UserRepository) : ViewModel() {
    
    val users: Flow<List<User>> = repository.getAllFlow()
    
    fun insert(user: User) {
        viewModelScope.launch {
            repository.insert(user)
        }
    }
    
    fun delete(user: User) {
        viewModelScope.launch {
            repository.delete(user)
        }
    }
}

class UserViewModelFactory(private val repository: UserRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return UserViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

7.3 导出Schema #

kotlin
@Database(
    entities = [User::class],
    version = 1,
    exportSchema = true
)
abstract class AppDatabase : RoomDatabase()

在build.gradle中配置:

kotlin
android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments["room.schemaLocation"] = "$projectDir/schemas"
            }
        }
    }
}

八、总结 #

本章详细介绍了Room数据库:

  1. Room的基本概念和优势
  2. Entity实体类的定义
  3. DAO数据访问对象的创建
  4. Database数据库的配置
  5. 数据库迁移的实现
  6. TypeConverter类型转换
  7. 最佳实践

Room是Android推荐的数据库解决方案,相比原生SQLite更加安全便捷,推荐在新项目中使用。

最后更新:2026-03-26