Come si utilizzano le istruzioni preparate in SQlite in Android?
Come si utilizzano le istruzioni preparate in SQlite in Android?
Risposte:
Uso sempre dichiarazioni preparate in Android, è abbastanza semplice:
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();
SQLiteStatement.bindXXX()
ha un indice basato su 1, non basato su 0 come la maggior parte di essi.
Per le istruzioni SQLite preparate in Android c'è SQLiteStatement . Le istruzioni preparate consentono di accelerare le prestazioni (soprattutto per le istruzioni che devono essere eseguite più volte) e aiutano anche a evitare attacchi di iniezione. Vedi questo articolo per una discussione generale sulle dichiarazioni preparate.
SQLiteStatement
è pensato per essere utilizzato con istruzioni SQL che non restituiscono più valori. (Ciò significa che non li useresti per la maggior parte delle query.) Di seguito sono riportati alcuni esempi:
String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();
Il execute()
metodo non restituisce un valore, quindi è appropriato da utilizzare con CREATE e DROP ma non è inteso per essere utilizzato con SELECT, INSERT, DELETE e UPDATE perché questi restituiscono valori. (Ma vedi questa domanda .)
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();
Si noti che il executeInsert()
metodo viene utilizzato anziché execute()
. Ovviamente non vorresti inserire sempre le stesse cose in ogni riga. Per questo puoi usare le associazioni .
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();
Di solito usi affermazioni preparate quando vuoi ripetere velocemente qualcosa (come un INSERT) molte volte. L'istruzione preparata fa in modo che l'istruzione SQL non debba essere analizzata e compilata ogni volta. Puoi velocizzare ulteriormente le cose utilizzando le transazioni . Ciò consente di applicare tutte le modifiche contemporaneamente. Ecco un esempio:
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();
}
Dai un'occhiata a questi collegamenti per ulteriori informazioni utili sulle transazioni e per velocizzare gli inserimenti nel database.
Questo è un semplice esempio. Puoi anche applicare i concetti della sezione precedente.
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();
Il executeUpdateDelete()
metodo può essere utilizzato anche per le istruzioni DELETE ed è stato introdotto nell'API 11. Vedere questa domanda e risposta .
Ecco un esempio.
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 si esegue una query, si desidera ripristinare un cursore con molte righe. Non è questo ciò che SQLiteStatement
serve, però. Non esegui una query con esso a meno che tu non abbia bisogno solo di un risultato semplice, come il numero di righe nel database, che puoi fare consimpleQueryForLong()
String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();
Di solito eseguirai il query()
metodo di SQLiteDatabase per ottenere un cursore.
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);
Vedi questa risposta per maggiori dettagli sulle query.
clearBindings()
non li imposta solo su null
(vedi il codice sorgente ). Lo considero come un ripulire lo stato in modo che nulla lo stia influenzando dal ciclo precedente. Forse non è necessario, però. Sarei felice per qualcuno che sa commentare.
Se vuoi un cursore al ritorno, potresti prendere in considerazione qualcosa del genere:
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"));
Vedi questo frammento Genscripts nel caso in cui ne desideri uno più completo. Si noti che questa è una query SQL parametrizzata, quindi in sostanza è un'istruzione preparata.
L'esempio di jasonhudgins non funzionerà. Non puoi eseguire una query con stmt.execute()
e recuperare un valore (o un Cursor
).
È possibile precompilare solo istruzioni che non restituiscono alcuna riga (come un'istruzione di inserimento o di creazione di una tabella) o una singola riga e colonna (e utilizzare simpleQueryForLong()
o simpleQueryForString()
).