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数据库:
- Room的基本概念和优势
- Entity实体类的定义
- DAO数据访问对象的创建
- Database数据库的配置
- 数据库迁移的实现
- TypeConverter类型转换
- 最佳实践
Room是Android推荐的数据库解决方案,相比原生SQLite更加安全便捷,推荐在新项目中使用。
最后更新:2026-03-26