Room 数据库迁移与性能优化
从基础到生产环境实践
目录
Room 基础回顾
数据库迁移策略
性能优化技巧
常见问题解决
最佳实践总结
1. Room 基础回顾 核心组件
flowchart LR
DAO["DAO<br/>数据访问对象 (CRUD)"] --> Database["Database<br/>数据库持有者"]
Database --> Entity["Entity<br/>数据模型 (表)"]
DAO --> Runtime["Room Runtime"]
Database --> Runtime
基本实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @Entity(tableName = "users" ) data class User ( @PrimaryKey val id: Long , val name: String, val email: String, val createdAt: Long = System.currentTimeMillis() ) @Dao interface UserDao { @Query("SELECT * FROM users" ) fun getAllUsers () : Flow<List<User>> @Query("SELECT * FROM users WHERE id = :id" ) suspend fun getUserById (id: Long ) : User? @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertUser (user: User ) @Delete suspend fun deleteUser (user: User ) @Query("DELETE FROM users" ) suspend fun deleteAllUsers () } @Database( entities = [User::class], version = 1, exportSchema = true ) abstract class AppDatabase : RoomDatabase () { abstract fun userDao () : UserDao }
2. 数据库迁移策略 简单版本升级 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Database( entities = [User::class, Post::class], version = 2, // 升级版本号 exportSchema = true ) abstract class AppDatabase : RoomDatabase () { companion object { private const val DATABASE_NAME = "my_database" } abstract fun userDao () : UserDao companion object { @Volatile private var INSTANCE: AppDatabase? = null fun getInstance (context: Context ) : AppDatabase { return INSTANCE ?: synchronized(this ) { val instance = Room.databaseBuilder( context.applicationContext, AppDatabase::class .java, DATABASE_NAME ) .addMigrations(MIGRATION_1_2) .build() INSTANCE = instance instance } } } }
迁移示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 val MIGRATION_1_2 = object : Migration(1 , 2 ) { override fun migrate (database: SupportSQLiteDatabase ) { database.execSQL(""" CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, title TEXT NOT NULL, content TEXT NOT NULL, userId INTEGER NOT NULL, createdAt INTEGER NOT NULL, FOREIGN KEY(userId) REFERENCES users(id) ON DELETE CASCADE ) """ .trimIndent()) } } val MIGRATION_2_3 = object : Migration(2 , 3 ) { override fun migrate (database: SupportSQLiteDatabase ) { database.execSQL(""" ALTER TABLE users ADD COLUMN phone TEXT """ .trimIndent()) } } val MIGRATION_3_4 = object : Migration(3 , 4 ) { override fun migrate (database: SupportSQLiteDatabase ) { database.execSQL("ALTER TABLE users RENAME TO users_new" ) database.execSQL(""" CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, email TEXT NOT NULL, phone TEXT, createdAt INTEGER NOT NULL ) """ .trimIndent()) database.execSQL("INSERT INTO users SELECT * FROM users_new" ) database.execSQL("DROP TABLE users_new" ) } }
自动迁移 (Room 2.4+) 1 2 3 4 5 6 7 @Database( entities = [User::class], version = 2, exportSchema = true ) @AutoMigration(from = 1, to = 2) abstract class AppDatabase : RoomDatabase ()
3. 性能优化技巧 1) 避免主线程查询 1 2 3 4 5 6 7 8 9 10 class Repository (private val userDao: UserDao) { fun getUsers () : Flow<List<User>> = userDao.getAllUsers() fun getUsersSync () : List<User> { return userDao.getAllUsers() } }
2) 使用索引 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Entity( tableName = "users" , indices = [ // ✅ 添加索引加速查询 Index(value = ["email" ], unique = true), Index(value = ["createdAt" ]) ] ) data class User ( @PrimaryKey val id: Long , val name: String, val email: String, val createdAt: Long )
3) 只查询需要的字段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Dao interface UserDao { @Query("SELECT id, name FROM users" ) fun getAllUserNames () : Flow<List<UserName>> @Query("SELECT * FROM users" ) fun getAllUsers () : Flow<List<User>> @Query("SELECT * FROM users LIMIT :limit OFFSET :offset" ) suspend fun getUsersPaginated (limit: Int , offset: Int ) : List<User> } data class UserName (val id: Long , val name: String)
4) 使用 Paging 库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Dao interface UserDao { @Query("SELECT * FROM users ORDER BY createdAt DESC" ) fun getUsersPaged () : PagingSource<Int , User> } class Repository (private val userDao: UserDao) { fun getUsersPaged () : Flow<PagingData<User>> { return Pager( config = PagingConfig( pageSize = 20 , enablePlaceholders = false , prefetchDistance = 5 ), pagingSourceFactory = { userDao.getUsersPaged() } ).flow } } @Composable fun UserList (viewModel: UserViewModel = hiltViewModel() ) { val pager = viewModel.users.collectAsLazyPagingItems() LazyColumn { items(pager) { user -> UserItem(user = user!!) } } }
5) Room + Flow 协程优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Dao interface UserDao { @Query("SELECT * FROM users" ) fun getAllUsers () : Flow<List<User>> @Query("SELECT * FROM users WHERE id = :id" ) fun getUserById (id: Long ) : Flow<User?> } fun getUserNames () : Flow<List<String>> { return userDao.getAllUsers() .map { users -> users.map { it.name } } .distinctUntilChanged() }
4. 常见问题解决 问题 1: Entity 与表字段不匹配 1 2 3 4 5 6 7 8 9 10 11 12 @Entity(tableName = "users" ) data class User ( @PrimaryKey val id: Long , @ColumnInfo(name = "user_name" ) val name: String, @ColumnInfo(name = "user_email" ) val email: String )
问题 2: 关系型数据查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 data class UserWithPosts ( @Embedded val user: User, @Relation( parentColumn = "id" , entityColumn = "userId" ) val posts: List<Post> ) @Dao interface UserDao { @Transaction @Query("SELECT * FROM users WHERE id = :userId" ) fun getUserWithPosts (userId: Long ) : Flow<UserWithPosts> @Transaction @Query("SELECT * FROM users" ) fun getAllUsersWithPosts () : Flow<List<UserWithPosts>> }
问题 3: 多数据库实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Database(...) abstract class AppDatabase : RoomDatabase () { companion object { @Volatile private var INSTANCE: AppDatabase? = null fun getInstance (context: Context ) : AppDatabase { return INSTANCE ?: synchronized(this ) { val instance = Room.databaseBuilder(...) .build() INSTANCE = instance instance } } } } fun badGetDatabase (context: Context ) : AppDatabase { return Room.databaseBuilder(...) .build() }
5. 最佳实践总结 Database 封装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class DatabaseManager private constructor ( context: Context ) { private val database = Room.databaseBuilder( context.applicationContext, AppDatabase::class .java, "app_database" ) .addMigrations(*ALL_MIGRATIONS) .fallbackToDestructiveMigration() .build() val userDao: UserDao get () = database.userDao() val postDao: PostDao get () = database.postDao() companion object { @Volatile private var INSTANCE: DatabaseManager? = null fun getInstance (context: Context ) : DatabaseManager { return INSTANCE ?: synchronized(this ) { val instance = DatabaseManager(context) INSTANCE = instance instance } } } } @Module @InstallIn(SingletonComponent::class) object DatabaseModule { @Provides fun provideDatabase (context: Context ) : AppDatabase { return Room.databaseBuilder(...) .build() } @Provides fun provideUserDao (database: AppDatabase ) : UserDao { return database.userDao() } }
Flow vs LiveData 选择
场景
推荐
Compose 中使用
Flow + collectAsStateWithLifecycle
需要在 Activity/Fragment 中观察
LiveData
需要跨进程通信
LiveData
纯 Kotlin 项目
Flow
与协程集成
Flow
性能优化检查清单
优化项
状态
添加索引
✅
只查询需要的字段
✅
使用 Paging 分页
✅
使用 Flow 而非 LiveData
✅
单例数据库实例
✅
添加迁移策略
✅
总结 Room 性能核心:
索引 : 为常用查询字段添加索引
分页 : 大数据集使用 Paging 库
协程 : 使用 Flow + 协程
单例 : 数据库实例只创建一次
迁移 : 使用 Migration 管理版本
相关文章 :