正如Android文档所说,rawQuery方法的selectionArgs参数被解析为字符串.
SQLiteDatabase.rawQuery(String sql, String[] selectionArgs)
selectionArgs: You may include ?s in where clause in the query,
which will be replaced by the values from selectionArgs.
The values will be bound as Strings.
但是今天,我遇到了一个问题,这个问题花了我很大的一天.想象一下以下查询:
SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= 15
COLUMN_A是INTEGER.该表有大约10行符合该标准.在数据库编辑器上运行查询,结果始终是正确的,但是,在智能手机上,语句始终不返回任何行.
一段时间后,将查询更改为:
SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= '15'
并且编辑器没有返回任何行,就像Android一样.因此,将查询更改为:
SELECT * FROM TABLE_A WHERE CAST(IFNULL(COLUMN_A, 0) as INTEGER) >= '15'
解决了这个问题.另一项测试是:
SELECT * FROM TABLE_A WHERE COLUMN_A >= '15'
还,返回了正确的结果.
这似乎是一个问题,涉及Android使用IFNULL子句将参数绑定到查询(作为字符串)的方式.
那么,有人知道为什么会这样吗?如果不在查询中使用CAST,是否有任何建议可以解决这个问题?
解决方法:
将值绑定到查询的原因是为了防止SQL-injection attack.
基本上,您将查询(包括占位符)发送到您的数据库并说“我的下一个查询将是这种形式,而不是其他!”.当攻击者然后注入不同的查询字符串(例如,通过表单字段)时,数据库会说“嘿,那不是你要发送的查询!”并向你抛出一个错误.
Since commands (which can be injected into your actual query as show in the linked article) are strings, the string-datatype is the more “dangerous” one.
If a user tries to inject some code into your field which should only take numbers and you try to cast/parse the input to an integer (before putting the value into your query), you’ll get an exception right away. With a string, there is no such kind security. Therefor, they have to be escaped probably.
That might be the reason that the bind values are all interpreted as strings.
以上是假的!如果您绑定的参数是字符串或整数并不重要,它们都同样危险.此外,在代码中预先检查您的值会产生大量的样板代码,容易出错并且不灵活!
为了防止您的应用程序进行SQL注入并加速多个数据库写入操作(使用具有不同值的相同查询),您应该使用“预准备语句”.在Android SDK中写入数据库的正确类是SQLiteStatement.
要创建预准备语句,请使用SQLiteDatabase对象的compileStatement()-method并使用正确的bindXX()方法(从SQLiteProgram继承)绑定相应的值(将用查询中的?标记替换):
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO SomeTable (name, age) values (?,?)");
// Careful! The index begins at 1, not 0 !!
stmt.bindString(1, "Jon");
stmt.bindLong(2, 48L);
stmt.execute();
// Also important! Clean up after yourself.
stmt.close();
从这个较老的问题中取出的例子:How do I use prepared statements in SQlite in Android?
遗憾的是,SQLiteStatement没有返回Cursor的重载,所以你不能将它用于SELECT语句.对于那些,您可以使用rawQuery(String,String []) – SQLiteDatabase的方法:
int number = getNumberFromUser();
String[] arguments = new String[]{String.valueOf(number)};
db.rawQuery("SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= ?", arguments);
请注意,rawQuery() – 方法为参数值采用String数组.这实际上没关系,SQLite会自动转换为正确的类型.只要字符串表示与SQL查询中的预期相同,就可以了.
【说明】:本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!