Android Room 的可选查询参数
Posted
技术标签:
【中文标题】Android Room 的可选查询参数【英文标题】:Optional query parameters for Android Room 【发布时间】:2019-02-01 09:48:46 【问题描述】:我有以下带有查询的 DAO:
@Dao
public interface BaseballCardDao
@Query(
"SELECT * FROM baseball_cards " +
"WHERE brand LIKE :brand " +
" AND year = :year " +
" AND number LIKE :number " +
" AND player_name LIKE :playerName " +
" AND team LIKE :team"
)
LiveData<List<BaseballCard>> getBaseballCards(
String brand, int year, String number, String playerName, String team
);
String
参数是“可选的”,因为LIKE
运算符我可以传递"%%"
来匹配所有行。但我不能用year
做到这一点,因为它是int
。一种解决方案是添加两种不同的@Query
方法,一种带有int year
参数,另一种没有。有没有更优雅的方法来使用 Room 的 @Query
创建可选参数?
【问题讨论】:
如果year
是可选的,您的方法签名是什么?你会保留这个签名并使用一些神奇的year
值来表示“忽略年份”吗?如果是这样,假设-1
是您的魔法值,您也许可以执行AND (year = :year OR -1 = :year)
之类的操作。
@CommonsWare 是的,保留签名和修改查询符合我的想法。你会把它作为答案发布吗?
【参考方案1】:
这是一个迟到的答案,但正如我最近遇到的那样,我想与那些正在寻找它的人分享我的简单(但愚蠢!)技巧。
正如@CommonsWare 所说,我们可以添加一个OR
语句来检查它是否为空,然后简单地使我们的可选参数可以为空并为它们传递null
。
例如,您的查询如下所示:
@Dao
public interface BaseballCardDao
@Query(
"SELECT * FROM baseball_cards " +
"WHERE (:brand IS NULL OR brand LIKE :brand)" +
" AND (:year IS NULL OR year = :year)" +
" AND (:number IS NULL OR number LIKE :number)" +
" AND (:playerName IS NULL OR player_name LIKE :playerName)" +
" AND (:team IS NULL OR team LIKE :team)"
)
LiveData<List<BaseballCard>> getBaseballCards(
@Nullable String brand, @Nullable Integer year, @Nullable String number, @Nullable String playerName, @Nullable String team
);
或者使用 kotlin 和可选参数进行更多声明:
@Query(
"""SELECT * FROM baseball_cards
WHERE (:brand IS NULL OR brand LIKE :brand)
AND (:year IS NULL OR year = :year)
AND (:number IS NULL OR number LIKE :number)
AND (:playerName IS NULL OR player_name LIKE :playerName)
AND (:team IS NULL OR team LIKE :team)"""
)
fun getBaseballCards(
brand: String? = null,
year: Int? = null,
number: String? = null,
playerName: String? = null,
team: String? = null
): LiveData<List<BaseballCard>>
编辑: 请考虑此解决方案对于不可为空的字段很有用。如果该字段可以为空,并且您想查找该字段没有值的记录,这不是正确的查询方式,您可以考虑创建动态查询。
【讨论】:
在我的原始代码中,我使用了int year
而不是Integer year
。在这里使用包装器可能是比 CommonsWare 建议的 SQL hack 更好的解决方案。
另外,您是否有理由不修改brand LIKE :brand
以添加NULL 检查?
这是一种糟糕的方法,因为它可能会弄乱整个结果集。这个问题实际上要求一种“更优雅的方式”......现在请向我解释这比定义单个方法并在外部切换它们更优雅吗?
@momt99 谢谢你的建议,不幸的是不能让它与房间一起工作 - 在我的情况下,(COALESCE(NULL, :myArray ) IS NULL)
在运行时(当我检查日志时)变成(COALESCE(NULL, ) IS NULL)
,以防我提供空数组作为 myArray争论。我发现的唯一可行方法是传递只有一个元素的“特殊”数组,然后将其比较为 `WHERE (:myArray) = (-1) OR (col IN (:myArray))。我猜这是非常丑陋和低效的。另一种方法可能是将“myArray”大小作为单独的参数传递。
@momt99 不错的技巧,但可能会返回错误的结果,如上所述。想象一下这种情况:您想获取所有“品牌”为空的项目;使用这种方法,“品牌”条件将被忽略(“:品牌 IS NULL”已满足,检查“或品牌 LIKE :品牌”被省略),您将获得所有商品,包括“品牌 = 空”和“品牌” != null'。但是对于实体中的“品牌”列被定义为不可为空的情况应该可以正常工作。以上是关于Android Room 的可选查询参数的主要内容,如果未能解决你的问题,请参考以下文章