如何在Room中忽略WHERE子句的部分筛选条件?
解决Room多条件筛选时忽略部分条件的问题
方法1:在SQL里加判断,跳过空参数的筛选
直接修改@Query语句,给每个可选条件加判断:如果参数是null,就不应用这个筛选条件。Room支持在SQL里写这种逻辑。
就你的场景,修改后的查询是这样:
@Query(""" SELECT * FROM Transactions WHERE (:account_ids IS NULL OR account_id IN (:account_ids)) AND (:category_ids IS NULL OR category_id IN (:category_ids)) """) fun getTransactions(account_ids: List<Long>?, category_ids: List<Long>?): Flow<List<Transaction>>
这样你传category_ids = null时,(:category_ids IS NULL)为true,这部分条件等于没生效,只会按账户筛选数据。
注意:要是传空列表[],category_id IN (:category_ids)会变成category_id IN (),这在SQL里是非法的,所以建议不想用某个条件时就传null,别传空列表。如果业务里难免会传空列表,可以再加一层判断:
@Query(""" SELECT * FROM Transactions WHERE (:account_ids IS NULL OR :account_ids = '[]' OR account_id IN (:account_ids)) AND (:category_ids IS NULL OR :category_ids = '[]' OR category_id IN (:category_ids)) """) fun getTransactions(account_ids: List<Long>?, category_ids: List<Long>?): Flow<List<Transaction>>
不过这个方法依赖Room把空列表转成字符串[]的逻辑,更稳妥的是调用方法前把空列表转成null。
方法2:用动态QueryBuilder构建查询
如果条件特别多,动态拼SQL会更灵活。用Room的@RawQuery注解,结合SupportSQLiteQueryBuilder来按需加条件:
fun getTransactions(accountIds: List<Long>?, categoryIds: List<Long>?): Flow<List<Transaction>> { val queryBuilder = SupportSQLiteQueryBuilder.builder("Transactions") // 有账户条件就加进去 accountIds?.takeIf { it.isNotEmpty() }?.let { queryBuilder.selection("account_id IN (?)", arrayOf(it.joinToString(","))) } // 有分类条件就加进去,已有其他条件时用AND连接 categoryIds?.takeIf { it.isNotEmpty() }?.let { val existingSelection = queryBuilder.selection val newSelection = if (existingSelection.isNullOrEmpty()) { "category_id IN (?)" } else { "$existingSelection AND category_id IN (?)" } val existingArgs = queryBuilder.selectionArgs ?: emptyArray() queryBuilder.selection(newSelection, existingArgs + arrayOf(it.joinToString(","))) } return transactionDao.getTransactionsRaw(queryBuilder.create()) } // 定义RawQuery方法 @RawQuery(observedEntities = [Transaction::class]) fun getTransactionsRaw(query: SupportSQLiteQuery): Flow<List<Transaction>>
这种方式只会把非空且非空的条件加到WHERE子句里,完全不需要的条件就不会出现,避免了SQL语法错误。
方法3:给参数设默认值简化调用
可以给方法参数设默认值为null,这样调用的时候只传需要的条件就行:
@Query(""" SELECT * FROM Transactions WHERE (:account_ids IS NULL OR account_id IN (:account_ids)) AND (:category_ids IS NULL OR category_id IN (:category_ids)) """) fun getTransactions(account_ids: List<Long>? = null, category_ids: List<Long>? = null): Flow<List<Transaction>>
比如只想按账户筛选,直接这么调用:getTransactions(account_ids = listOf(1L, 2L)),分类条件会自动忽略。
内容的提问来源于stack exchange,提问作者Oleh Romanenko
相关产品推荐
相关产品推荐

