Как использовать подготовленные операторы в SQlite в Android?
Как использовать подготовленные операторы в SQlite в Android?
Ответы:
Я постоянно использую подготовленные операторы в Android, это довольно просто:
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();
SQLiteStatement.bindXXX()индекс основан на 1, а не на 0, как у большинства.
Для подготовленных операторов SQLite в Android есть SQLiteStatement . Подготовленные операторы помогают повысить производительность (особенно для операторов, которые необходимо выполнять несколько раз), а также помогают избежать атак с использованием инъекций. См. Эту статью для общего обсуждения подготовленных операторов.
SQLiteStatementпредназначен для использования с операторами SQL, которые не возвращают несколько значений. (Это означает, что вы не будете использовать их для большинства запросов.) Ниже приведены несколько примеров:
String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();
Этот execute()метод не возвращает значение, поэтому его можно использовать с CREATE и DROP, но он не предназначен для использования с SELECT, INSERT, DELETE и UPDATE, поскольку эти возвращаемые значения. (Но посмотрите на этот вопрос .)
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();
Обратите внимание, что executeInsert()метод используется, а не execute(). Конечно, вы не хотели бы всегда вводить одни и те же данные в каждую строку. Для этого можно использовать привязки .
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
SQLiteStatement statement = db.compileStatement(sql);
int intValue = 57;
String stringValue = "hello";
statement.bindLong(1, intValue); // 1-based: matches first '?' in sql string
statement.bindString(2, stringValue); // matches second '?' in sql string
long rowId = statement.executeInsert();
Обычно вы используете подготовленные операторы, когда хотите быстро повторить что-то (например, INSERT) много раз. Подготовленный оператор делает так, что оператор SQL не нужно каждый раз анализировать и компилировать. Вы можете ускорить процесс еще больше, используя транзакции . Это позволяет применить все изменения сразу. Вот пример:
String stringValue = "hello";
try {
db.beginTransaction();
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
SQLiteStatement statement = db.compileStatement(sql);
for (int i = 0; i < 1000; i++) {
statement.clearBindings();
statement.bindLong(1, i);
statement.bindString(2, stringValue + i);
statement.executeInsert();
}
db.setTransactionSuccessful(); // This commits the transaction if there were no exceptions
} catch (Exception e) {
Log.w("Exception:", e);
} finally {
db.endTransaction();
}
Ознакомьтесь с этими ссылками, чтобы получить более подробную информацию о транзакциях и ускорении вставки в базу данных.
Это простой пример. Вы также можете применить концепции из раздела выше.
String sql = "UPDATE table_name SET column_2=? WHERE column_1=?";
SQLiteStatement statement = db.compileStatement(sql);
int id = 7;
String stringValue = "hi there";
statement.bindString(1, stringValue);
statement.bindLong(2, id);
int numberOfRowsAffected = statement.executeUpdateDelete();
Этот executeUpdateDelete()метод также может использоваться для операторов DELETE и был представлен в API 11. См. Эти вопросы и ответы .
Вот пример.
try {
db.beginTransaction();
String sql = "DELETE FROM " + table_name +
" WHERE " + column_1 + " = ?";
SQLiteStatement statement = db.compileStatement(sql);
for (Long id : words) {
statement.clearBindings();
statement.bindLong(1, id);
statement.executeUpdateDelete();
}
db.setTransactionSuccessful();
} catch (SQLException e) {
Log.w("Exception:", e);
} finally {
db.endTransaction();
}
Обычно, когда вы запускаете запрос, вы хотите вернуть курсор с большим количеством строк. Однако это не то SQLiteStatement, для чего. Вы не запускаете с ним запрос, если вам не нужен только простой результат, например количество строк в базе данных, что вы можете сделать сsimpleQueryForLong()
String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();
Обычно вы запускаете query()метод SQLiteDatabase, чтобы получить курсор.
SQLiteDatabase db = dbHelper.getReadableDatabase();
String table = "table_name";
String[] columnsToReturn = { "column_1", "column_2" };
String selection = "column_1 =?";
String[] selectionArgs = { someValue }; // matched to "?" in selection
Cursor dbCursor = db.query(table, columnsToReturn, selection, selectionArgs, null, null, null);
См. Этот ответ для получения более подробной информации о запросах.
clearBindings()не просто устанавливает их null(см. Исходный код ). Я смотрю на это как на очистку состояния, чтобы ничего не влияло на него из предыдущего цикла. Хотя, возможно, в этом нет необходимости. Буду рад, если кто-нибудь прокомментирует.
Если вам нужен курсор при возврате, вы можете рассмотреть что-то вроде этого:
SQLiteDatabase db = dbHelper.getWritableDatabase();
public Cursor fetchByCountryCode(String strCountryCode)
{
/**
* SELECT * FROM Country
* WHERE code = US
*/
return cursor = db.query(true,
"Country", /**< Table name. */
null, /**< All the fields that you want the
cursor to contain; null means all.*/
"code=?", /**< WHERE statement without the WHERE clause. */
new String[] { strCountryCode }, /**< Selection arguments. */
null, null, null, null);
}
/** Fill a cursor with the results. */
Cursor c = fetchByCountryCode("US");
/** Retrieve data from the fields. */
String strCountryCode = c.getString(cursor.getColumnIndex("code"));
/** Assuming that you have a field/column with the name "country_name" */
String strCountryName = c.getString(cursor.getColumnIndex("country_name"));
См. Этот фрагмент Genscripts, если вам нужен более полный. Обратите внимание, что это параметризованный SQL-запрос, поэтому, по сути, это подготовленный оператор.
Пример с jasonhudgins не работает. Вы не можете выполнить запрос stmt.execute()и вернуть значение (или Cursor).
Вы можете предварительно скомпилировать только операторы, которые либо не возвращают никаких строк (например, оператор вставки или создания таблицы), либо одну строку и столбец (и используйте simpleQueryForLong()или simpleQueryForString()).