在 Android 中使用准备好的语句进行查询?

Posted

技术标签:

【中文标题】在 Android 中使用准备好的语句进行查询?【英文标题】:Queries with prepared statements in Android? 【发布时间】:2012-04-09 01:38:00 【问题描述】:

android 中,android.database.sqlite.SQLiteStatement 允许我在 SQLite 中使用准备好的语句来避免注入攻击。它的execute 方法适用于创建/更新/删除操作,但是对于返回游标之类的查询似乎没有任何方法。

现在在 ios 中,我可以创建 sqlite3_stmt* 类型的准备好的语句并将它们用于查询,所以我知道这不是 SQLite 的限制。如何在 Android 中使用准备好的语句执行查询?

【问题讨论】:

【参考方案1】:

准备好的语句允许你做两件事

提高性能,因为数据库不需要每次都解析语句 在语句中绑定和转义参数,这样可以避免注入攻击

我不确切知道 Android 的 SQLite 实现在何处/何时实际使用 sqlite3_prepare(afiak 不是 sqlite3_prepare_v2 - 请参阅 here),但它确实使用了它,否则您无法获得 Reached MAX size for compiled-sql statement cache errors。

因此,如果您想查询数据库,您必须依赖实现,我不知道使用SQLiteStatement 来做到这一点。

关于注入安全性,每个数据库查询、插入等方法都有(有时是替代的)版本,允许您绑定参数。

例如如果你想得到一个Cursor

SELECT * FROM table WHERE column1='value1' OR column2='value2'

Cursor SQLiteDatabase#rawQuery(

String sql, : 完整的 SELECT 声明,可以在任何地方包含 ? String[] selectionArgs :替换 ? 的值列表,按它们出现的顺序排列

)

Cursor c1 = db.rawQuery(
    "SELECT * FROM table WHERE column1=? OR column2=?",
    new String[] "value1", "value2"
);

Cursor SQLiteDatabase#query (

String table, : 表名,可以包含JOINString[] columns, :所需列的列表,null = * String selection, : WHERE 没有WHERE 的子句可以/应该包括? String[] selectionArgs, : 替换 ? 的值列表,按它们出现的顺序排列 String groupBy, : GROUP BY 子句不带 GROUP BY String having, : HAVING 子句不带 HAVING String orderBy : ORDER BY 子句 w/o ORDER BY

)

Cursor c2 = db.query("table", null, 
     "column1=? OR column2=?", 
      new String[] "value1", "value2",
      null, null, null);

通过 ContentProviders - 这种情况略有不同,因为您与抽象提供者而不是数据库进行交互。确实不能保证有支持ContentProvider 的sqlite 数据库。因此,除非您知道有哪些列/提供程序在内部如何工作,否则您应该遵守文档中的说明。

Cursor ContentResolver#query(

Uri uri, : 表示数据源的 URI(内部翻译成表格) String[] projection, :所需列的列表,null = * String selection, : WHERE 子句没有 WHERE 可以/应该包括 ? String[] selectionArgs, :替换 ? 的值列表,按它们出现的顺序排列 String sortOrder : ORDER BY 子句 w/o ORDER BY

)

Cursor c3 = getContentResolver().query(
     Uri.parse("content://provider/table"), null,
     "column=? OR column2=?", 
      new String[] "value1", "value2",
      null);

提示:如果您想在此处添加LIMIT,可以将其添加到ORDER BY 子句中:

String sortOrder = "somecolumn LIMIT 5";

或者根据ContentProvider的实现将它作为参数添加到Uri

Uri.parse("content://provider/table?limit=5");
// or better via buildUpon()
Uri audio = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
audio.buildUpon().appendQueryParameter("limit", "5");

在所有情况下,? 都将替换为您在绑定参数中输入的转义版本。

? + "hack'me" = 'hack''me'

【讨论】:

出色的帖子,谢谢,对我帮助很大。像这样的时代让我觉得我们需要能够多次修改。 即使这个答案很老,是否有一种解决方法可以绑定除字符串之外的任何内容。我的查询请求绑定一个数值,但 SqliteDatabase 中的每个方法都使用一个字符串数组。如果我自己创建 PreparedStatement,我可以执行查询来恢复光标...我不敢相信没有解决方案,但我还没有找到它。 @AxelH 解决办法是一切都可以用字符串表示。例如使用String.valueOf(yourNumericThingHere) 当然可以,但是我的查询要求进行数值比较示例“columnX 使用带有 whereClause 'columnX

以上是关于在 Android 中使用准备好的语句进行查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何在另一个准备好的语句中使用 PreparedStatement 作为子查询

PHP:准备好的语句无法从单引号中逃脱

安全地使用准备好的语句查询数据库

如何在 Android sqlite 中使用准备好的语句? [复制]

休眠和准备好的语句

什么时候在 PHP Mysqli 中使用准备好的语句? - 用户表单搜索输入与选择查询