如何将INTEGER / NULL绑定到? Android中的rawQuery selectionArgs占位符?

时间:2015-03-10 20:28:48

标签: java android sql sqlite sql-injection

以下任何内容都可以更改,以达到可行的解决方案! 我可以完全控制以下所有架构/数据/查询/代码,所以任何合理的欢迎改进:我正在寻找一个简单/清洁/到点的解决方案。例如,制作两个不同的查询(= ?is null)是最后的手段

问题

我想更改以下代码,以便我可以致电listCategoriesIn(1)listCategoriesIn(null),他们都会给出正确的预期结果。 我无法listCategoriesIn(null)使用WHERE这样的c.parent = ?条款。

  • 是否可以将INTEGERNULL绑定到= ?
  • 如何修改WHERE子句以使其适用于这两种情况?
  • 我还可以改变什么来使其发挥作用?

CREATE TABLE Category (
    _id         INTEGER      NOT NULL,
    name        VARCHAR      NOT NULL,
    parent      INTEGER          NULL --< important bit
        CONSTRAINT fk_Category_parent
            REFERENCES Category(_id)
            ON UPDATE CASCADE
            ON DELETE CASCADE,
    PRIMARY KEY(_id AUTOINCREMENT),
    UNIQUE(name)
);

样本数据

INSERT INTO Category
                  (_id, parent, name)
          SELECT     0,   NULL, 'cat0'         --< expected for listCategoriesIn(null)
    UNION SELECT     1,   NULL, 'cat1'         --< expected for listCategoriesIn(null)
    UNION SELECT    11,      1,   'cat1-1'     --< expected for listCategoriesIn(1)
    UNION SELECT    12,      1,   'cat1-2'     --< expected for listCategoriesIn(1)
    UNION SELECT   121,     12,     'cat1-2-1'
    UNION SELECT   122,     12,     'cat1-2-2'
    UNION SELECT    13,      1,   'cat1-3'     --< expected for listCategoriesIn(1)
    UNION SELECT   131,     13,     'cat1-3-1'
    UNION SELECT     2,   NULL, 'cat2'         --< expected for listCategoriesIn(null)
    UNION SELECT    21,      2,   'cat2-1'
    UNION SELECT     3,   NULL, 'cat3'         --< expected for listCategoriesIn(null)
;

查询

IRL我使用了更复杂的视频,子查询和多个JOIN。

select
    c.*,
    (select count() from Category where parent = c._id) as count
from Category c
where c.parent = ? --< important bit
;

错误的代码#1

public Cursor listCategoriesIn(SQLiteDatabase db, Long categoryID) {
    // public Cursor SQLiteDatabse.rawQuery(String sql, String[] selectionArgs);
    return db.rawQuery(CATEGORY_QUERY, new String[] {
        String.valueOf(categoryID)
    });
}
  

listCategoriesIn(1):工作正常

     

listCategoriesIn(null):生成的Cursor为空,可能绑定= 'null'= NULL

错误的代码#2

public Cursor listCategoriesIn(SQLiteDatabase db, Long categoryID) {
    // public Cursor SQLiteDatabse.rawQuery(String sql, String[] selectionArgs);
    return db.rawQuery(CATEGORY_QUERY, new String[] {
        categoryID == null? null : categoryID.toString()
    });
}
  

listCategoriesIn(1):工作正常

     

listCategoriesIn(null):   java.lang.IllegalArgumentException:索引1处的绑定值为null

2 个答案:

答案 0 :(得分:0)

我认为当字段为NULL时,您需要发送不同的SQL语句。

    public Cursor listCategoriesIn(SQLiteDatabase db, Long categoryID) {
        if(categoryID == null)
            return db.rawQuery(CATEGORY_QUERY_NULL, null);
        else
            return db.rawQuery(CATEGORY_QUERY, new String[] { categoryID.toString() });
   }

其中CATEGORY_QUERY_NULL与CATEGORY_QUERY相同,但使用c.parent IS NULL代替c.parent = ?。有关详细信息,请参阅此answer

答案 1 :(得分:0)

在睡觉之后开始新的一天我有顿悟,可以不重复查询:

where c.parent = ? or (? = 'null' and c.parent is null)

但是我们需要复制参数,这里是相应的Java调用:

public Cursor listCategoriesIn(SQLiteDatabase db, Long categoryID) {
    return db.rawQuery(CATEGORY_QUERY, new String[] {
        String.valueOf(categoryID), String.valueOf(categoryID)
    });
}

categoryIDnull时,没有NPE,因为它绑定为"null"(请参阅valueOf)。将null->'null'传递给驱动程序会激活WHERE子句的第二部分,当然INTEGER个值中没有一个= 'null',所以第一部分不会播放。

这个技巧有效,因为rawQuery binds everything as Strings和SQLite引擎可以处理INTEGERTEXT中的数字进行比较。我确定可以在SQLite Datatypes documentation

中找到详细信息