准备好的语句允许你做两件事
- 提高性能,因为数据库不需要每次都解析语句
- 在语句中绑定和转义参数,这样可以避免注入攻击
我不确切知道 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, : 表名,可以包含JOIN等
-
String[] 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'