ROOM数据库数据过滤实现是否高效?求最优方案
基于Room的待办APP过滤逻辑效率分析与优化建议
问题背景
我开发了一款基于ROOM数据库的待办事项列表APP,支持按标题、优先级或描述过滤列表。想了解ViewModel中实现的过滤逻辑是否高效?若存在优化空间,最优实现方式是什么?
当前实现代码
DAO层
@Dao interface ToDoDao { @Insert(ToDoData::class, onConflict = OnConflictStrategy.IGNORE) suspend fun insert(toDoData: ToDoData) @Delete(ToDoData::class) suspend fun delete(item: ToDoData) @Update(ToDoData::class,onConflict = OnConflictStrategy.IGNORE) suspend fun update(item: ToDoData) @Query("DELETE FROM TODO_TABLE") suspend fun deleteAllData() @Query("SELECT * FROM TODO_TABLE ORDER BY id ASC ") fun getAllData(): LiveData<List<ToDoData>> @Query("SELECT * FROM TODO_TABLE ORDER BY CASE WHEN priority LIKE 'H%' THEN 1 WHEN priority LIKE 'M%' THEN 2 WHEN priority LIKE 'L%' THEN 3 END") fun getAllDataSortedByHighPriority(): LiveData<List<ToDoData>> @Query("SELECT * FROM TODO_TABLE ORDER BY CASE WHEN priority LIKE 'L%' THEN 1 WHEN priority LIKE 'M%' THEN 2 WHEN priority LIKE 'H%' THEN 3 END") fun getAllDataSortedByLowPriority(): LiveData<List<ToDoData>> @Query("SELECT * FROM TODO_TABLE WHERE title LIKE '%' || :titleContains || '%' AND priority IN (:prioritiesIn) AND description LIKE '%' || :descContains || '%' ORDER BY ID ASC") fun filterDataSortedByID(titleContains: String, prioritiesIn:Array<Priority>,descContains:String): LiveData<List<ToDoData>> }
Repository层
class ToDoRepository(private val dao: ToDoDao) { val allData: LiveData<List<ToDoData>> = dao.getAllData() val allDataSortedByHighPriority = dao.getAllDataSortedByHighPriority() val allDataSortedByLowPriority = dao.getAllDataSortedByLowPriority() suspend fun insert(item: ToDoData) { dao.insert(item) } suspend fun delete(item: ToDoData) { dao.delete(item) } suspend fun update(item: ToDoData) { dao.update(item) } suspend fun deleteAll(){ dao.deleteAllData() } fun filterDataSortedByID(titleContains: String, prioritiesIn:Array<Priority>,descContains:String): LiveData<List<ToDoData>>{ return dao.filterDataSortedByID(titleContains,prioritiesIn,descContains) } }
ViewModel层
class ToDoViewModel(context: Application) : AndroidViewModel(context) { private val toDoDao = ToDoDatabase.getDataBase(context.applicationContext).toDoDao() private val repository = ToDoRepository(toDoDao) private val filters: MutableLiveData<MutableMap<String, Any>> = MutableLiveData( mutableMapOf( "titleContains" to "", "prioritiesIn" to arrayOf(Priority.HIGH, Priority.MEDIUM, Priority.LOW), "descContains" to "", ) ) var titleContains: String get() = filters.value?.get("titleContains") as String set(value) { filters.value = filters.value?.apply { set("titleContains", value) } } var prioritiesIn: Array<Priority> get() = filters.value?.get("prioritiesIn") as Array<Priority> set(value) { filters.value = filters.value?.apply { set("prioritiesIn", value) } } var descContains: String get() = filters.value?.get("descContains") as String set(value) { filters.value = filters.value?.apply { set("descContains", value) } } val filteredData = filters.switchMap { _ -> if (titleContains.isEmpty() && descContains.isEmpty()) { repository.allData } else { repository.filterDataSortedByID(titleContains, prioritiesIn, descContains) } } fun insertData(item: ToDoData) { viewModelScope.launch { repository.insert(item) } } fun updateData(item: ToDoData) { viewModelScope.launch { repository.update(item) } } fun deleteData(item: ToDoData) { viewModelScope.launch { repository.delete(item) } } fun deleteAll() { viewModelScope.launch { repository.deleteAll() } } }
当前逻辑的优缺点
值得肯定的地方
- 过滤逻辑放在数据库层处理,利用SQLite的查询优化能力,比内存中过滤更高效,数据量大时优势明显。
- 用
switchMap监听过滤器变化,自动触发查询更新,符合LiveData响应式设计思路。 - 增加了简单判断:标题和描述过滤条件为空时直接返回全量数据,避免不必要的带条件查询。
可以优化的点
- 过滤器状态管理不安全:用
MutableMap<String, Any>存储过滤条件,每次取值都要强制类型转换,容易出现类型错误,代码可读性差。 - 优先级过滤有语法风险:若用户取消所有优先级选择,
prioritiesIn会是空数组,此时SQL中的IN ()会直接报错。 - SQL查询性能有优化空间:
LIKE '%xxx%'前后通配符查询无法利用索引,数据量大时查询速度变慢。- 优先级排序用
LIKE 'H%'匹配,效率低——如果priority是枚举类型,直接匹配枚举值更快。
- 过滤器更新冗余:每次修改单个过滤条件都要重新创建整个Map对象,可更高效地处理状态更新。
- 过滤与排序未结合:当前
filteredData仅支持按ID排序,无法让用户选择过滤后按优先级排序(如高优先在前)。
优化后的实现方案
1. 用数据类重构过滤器状态
用强类型数据类替代Map,类型安全且可读性更高:
// 定义过滤条件数据类 data class TodoFilters( val titleContains: String = "", val selectedPriorities: Set<Priority> = setOf(Priority.HIGH, Priority.MEDIUM, Priority.LOW), val descContains: String = "" ) // ViewModel中替换过滤器定义 private val filters = MutableLiveData(TodoFilters()) // 简化属性访问,无需强制类型转换 var titleContains: String get() = filters.value?.titleContains ?: "" set(value) { filters.value = filters.value?.copy(titleContains = value) ?: TodoFilters(titleContains = value) } var selectedPriorities: Set<Priority> get() = filters.value?.selectedPriorities ?: emptySet() set(value) { filters.value = filters.value?.copy(selectedPriorities = value) ?: TodoFilters(selectedPriorities = value) } var descContains: String get() = filters.value?.descContains ?: "" set(value) { filters.value = filters.value?.copy(descContains = value) ?: TodoFilters(descContains = value) }
2. 修复SQL查询问题
处理空优先级集合
修改DAO过滤查询,优先级集合为空时返回空结果,避免SQL语法错误:
@Query(""" SELECT * FROM TODO_TABLE WHERE (:titleEmpty = 1 OR title LIKE '%' || :titleContains || '%') AND (:prioritiesEmpty = 1 OR priority IN (:selectedPriorities)) AND (:descEmpty = 1 OR description LIKE '%' || :descContains || '%') ORDER BY ID ASC """) fun filterData( titleContains: String, titleEmpty: Int, selectedPriorities: Array<Priority>, prioritiesEmpty: Int, descContains: String, descEmpty: Int ): LiveData<List<ToDoData>>
Repository中调用时传入空判断参数:
fun filterData(filters: TodoFilters): LiveData<List<ToDoData>> { val titleEmpty = if (filters.titleContains.isEmpty()) 1 else 0 val prioritiesEmpty = if (filters.selectedPriorities.isEmpty()) 1 else 0 val descEmpty = if (filters.descContains.isEmpty()) 1 else 0 return dao.filterData( filters.titleContains, titleEmpty, filters.selectedPriorities.toTypedArray(), prioritiesEmpty, filters.descContains, descEmpty ) }
优化优先级排序查询
若Priority是枚举类型,直接匹配枚举值替代LIKE:
@Query(""" SELECT * FROM TODO_TABLE ORDER BY CASE priority WHEN :high THEN 1 WHEN :medium THEN 2 WHEN :low THEN 3 END ASC """) fun getAllDataSortedByHighPriority( high: Priority = Priority.HIGH, medium: Priority = Priority.MEDIUM, low: Priority = Priority.LOW ): LiveData<List<ToDoData>>
模糊查询性能优化(可选)
提升模糊查询速度可选择:
- 给
title和description添加全文索引,用MATCH替代LIKE(SQLite支持全文搜索)。 - 引导用户使用前缀搜索(如
'xxx%'),普通索引即可生效。
3. 结合过滤与排序
扩展过滤查询,支持传入排序参数,实现过滤后按优先级或ID排序:
@Query(""" SELECT * FROM TODO_TABLE WHERE (:titleEmpty = 1 OR title LIKE '%' || :titleContains || '%') AND (:prioritiesEmpty = 1 OR priority IN (:selectedPriorities)) AND (:descEmpty = 1 OR description LIKE '%' || :descContains || '%') ORDER BY CASE WHEN :sortByPriority = 1 THEN CASE priority WHEN :high THEN 1 WHEN :medium THEN 2 WHEN :low THEN 3 END ELSE id END ASC """) fun filterDataWithSort( titleContains: String, titleEmpty: Int, selectedPriorities: Array<Priority>, prioritiesEmpty: Int, descContains: String, descEmpty: Int, sortByPriority: Int, high: Priority = Priority.HIGH, medium: Priority = Priority.MEDIUM, low: Priority = Priority.LOW ): LiveData<List<ToDoData>>
4. 优化ViewModel的switchMap逻辑
直接使用filters当前值,避免重复访问属性,减少空指针风险:
val filteredData = filters.switchMap { currentFilters -> // 所有过滤条件为默认值时返回全量数据 val isDefault = currentFilters.titleContains.isEmpty() && currentFilters.descContains.isEmpty() && currentFilters.selectedPriorities == setOf(Priority.HIGH, Priority.MEDIUM, Priority.LOW) if (isDefault) { repository.allData } else { repository.filterData(currentFilters) } }
5. 可选:用Flow替代LiveData
若项目使用Kotlin协程,推荐用Flow替代LiveData,性能更好且支持更多操作符:
// DAO中返回Flow @Query(...) fun filterData(...): Flow<List<ToDoData>> // Repository中返回Flow fun filterData(filters: TodoFilters): Flow<List<ToDoData>> = dao.filterData(...) // ViewModel中转换为LiveData(或直接用Flow收集) val filteredData = filters.asFlow() .flatMapLatest { currentFilters -> val isDefault = currentFilters.titleContains.isEmpty() && currentFilters.descContains.isEmpty() && currentFilters.selectedPriorities == setOf(Priority.HIGH, Priority.MEDIUM, Priority.LOW) if (isDefault) repository.allData.asFlow() else repository.filterData(currentFilters) } .asLiveData(viewModelScope.coroutineContext)
内容的提问来源于stack exchange,提问作者Emz
相关产品推荐
相关产品推荐

