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 的可选查询参数的主要内容,如果未能解决你的问题,请参考以下文章

带有查询 Spring-Boot jpa 1.5 的可选参数

Spring boot:Query方法中的可选参数查询

compojure-api 的可选查询参数(具有默认值)

如何处理 Play 框架中的可选查询参数

WCF中URITemplate中的可选查询字符串参数?

ASP.NET Web API 中的可选查询字符串参数