Como faço para usar instruções preparadas em SQlite no Android?
Como faço para usar instruções preparadas em SQlite no Android?
Respostas:
Eu uso declarações preparadas no Android o tempo todo, é bastante simples:
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();
SQLiteStatement.bindXXX()
tem um índice baseado em 1, não baseado em 0 como a maioria dos outros.
Para instruções SQLite preparadas no Android, existe SQLiteStatement . As instruções preparadas ajudam a acelerar o desempenho (especialmente para instruções que precisam ser executadas várias vezes) e também ajudam a evitar ataques de injeção. Veja este artigo para uma discussão geral sobre as declarações preparadas.
SQLiteStatement
deve ser usado com instruções SQL que não retornam vários valores. (Isso significa que você não os usaria para a maioria das consultas.) Abaixo estão alguns exemplos:
String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();
O execute()
método não retorna um valor, portanto, é apropriado para ser usado com CREATE e DROP, mas não se destina a ser usado com SELECT, INSERT, DELETE e UPDATE porque esses valores retornam. (Mas veja esta pergunta .)
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();
Observe que o executeInsert()
método é usado em vez de execute()
. Claro, você não gostaria de inserir sempre as mesmas coisas em todas as linhas. Para isso, você pode usar ligações .
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();
Normalmente, você usa instruções preparadas quando deseja repetir algo rapidamente (como um INSERT) muitas vezes. A instrução preparada faz com que a instrução SQL não precise ser analisada e compilada todas as vezes. Você pode acelerar as coisas ainda mais usando transações . Isso permite que todas as alterações sejam aplicadas de uma vez. Aqui está um exemplo:
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();
}
Confira esses links para mais informações sobre transações e como agilizar as inserções no banco de dados.
Este é um exemplo básico. Você também pode aplicar os conceitos da seção acima.
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();
O executeUpdateDelete()
método também pode ser usado para instruções DELETE e foi introduzido na API 11. Veja este Q&A .
Aqui está um exemplo.
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();
}
Normalmente, quando você executa uma consulta, deseja obter um cursor de volta com muitas linhas. Mas não SQLiteStatement
é para isso. Você não executa uma consulta com ele, a menos que precise apenas de um resultado simples, como o número de linhas no banco de dados, o que pode ser feito comsimpleQueryForLong()
String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();
Normalmente, você executará o query()
método SQLiteDatabase para obter um cursor.
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);
Veja esta resposta para maiores detalhes sobre as dúvidas.
clearBindings()
não apenas os define como null
(consulte o código-fonte ). Eu vejo isso como uma limpeza do estado para que nada o influencie do loop anterior. Talvez isso não seja necessário. Eu ficaria feliz se alguém que sabe comentar.
Se você quiser um cursor no retorno, pode considerar algo assim:
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"));
Veja este snippet Genscripts caso queira um mais completo. Observe que esta é uma consulta SQL parametrizada, portanto, em essência, é uma instrução preparada.
O exemplo de jasonhudgins não funcionará. Você não pode executar uma consulta com stmt.execute()
e obter um valor (ou a Cursor
) de volta.
Você só pode pré-compilar instruções que não retornem nenhuma linha (como uma instrução de inserção ou criação de tabela) ou uma única linha e coluna (e use simpleQueryForLong()
ou simpleQueryForString()
).