Android Room的可选查询参数

11

我有以下带有查询的 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
    );
}

在使用LIKE操作符时,String参数是“可选”的,因为我可以传递"%%"来匹配所有行。但由于year是一个int类型,我不能这样做。解决方法是添加两个不同的@Query方法,一个带有int year参数,另一个没有。是否有更优雅的方法在Room's @Query中创建可选参数?


如果year是可选的,你的方法签名会是什么?你会保留这个签名并使用一些魔法值来表示“忽略年份”吗?如果是这样,你可以尝试像这样做:AND (year = :year OR -1 = :year),假设-1是你的魔法值。 - CommonsWare
@CommonsWare 是的,保留签名并修改查询是我考虑的方向。你会把它发表为答案吗? - Code-Apprentice
1个回答

13

虽然回答有些晚,但由于我最近也遇到了这个问题,我想分享一下我的简单(但愚蠢!)技巧,供那些正在寻找解决方案的人参考。如@CommonsWare所说,我们可以添加一个检查null的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>>

编辑: 请注意,此解决方案适用于非空字段。如果字段可为空,并且您想要查找没有该字段值的记录,则这不是正确的查询方式,您可以考虑动态查询创建。


@Code-Apprentice 不好意思,看来我错过了!感谢您的关注。已编辑。 - momvart
1
这是一种糟糕的方法,因为它可能会搞乱整个结果集。实际上,问题要求“更优雅的方式”...现在请向我解释,这种方法如何比定义单独的方法并在外部切换它们更加优雅? - Martin Zeitler
1
@momt99 感谢您的建议,不幸的是,我无法通过room使其工作 - 在我的情况下,如果我提供空数组作为myArray参数,则(COALESCE(NULL, :myArray ) IS NULL)在运行时(当我检查日志时)变成了(COALESCE(NULL, ) IS NULL)。我找到的唯一有效的方法是传递一个只有一个元素的“特殊”数组,并像WHERE (:myArray) = (-1) OR (col IN (:myArray))一样进行比较。我想这很丑陋而且效率低下。另一种方法可能是将'myArray'的大小作为单独的参数传递。 - XZen
1
@momt99 不错的技巧,但可能会返回错误的结果,如上所述。想象一下这种情况:您想获取所有“品牌”为空的项目;使用这种方法,“品牌”条件将被忽略(“:brand IS NULL”已满足,检查“OR brand LIKE:brand”的条件被省略),您将获得所有项目,其中包括“brand = null”和“brand!= null”。 但是,如果实体中的“brand”列定义为非空,则应该可以正常工作。 - ernazm
@ernazm 是的,你说得对。在可空情况下它不能正常工作。 - momvart
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接