Comment utiliser des instructions préparées dans SQlite sous Android?
Comment utiliser des instructions préparées dans SQlite sous Android?
Réponses:
J'utilise tout le temps des déclarations préparées sous Android, c'est assez simple:
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();
SQLiteStatement.bindXXX()
a un index basé sur 1, pas basé sur 0 comme la plupart des.
Pour les instructions SQLite préparées sous Android, il existe SQLiteStatement . Les instructions préparées vous aident à accélérer les performances (en particulier pour les instructions qui doivent être exécutées plusieurs fois) et à éviter les attaques par injection. Consultez cet article pour une discussion générale sur les déclarations préparées.
SQLiteStatement
est destiné à être utilisé avec des instructions SQL qui ne renvoient pas plusieurs valeurs. (Cela signifie que vous ne les utiliserez pas pour la plupart des requêtes.) Voici quelques exemples:
String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();
La execute()
méthode ne renvoie pas de valeur, il est donc approprié de l'utiliser avec CREATE et DROP, mais n'est pas destinée à être utilisée avec SELECT, INSERT, DELETE et UPDATE car ces valeurs retournent. (Mais voyez cette question .)
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();
Notez que la executeInsert()
méthode est utilisée plutôt que execute()
. Bien sûr, vous ne voudriez pas toujours entrer les mêmes choses dans chaque ligne. Pour cela, vous pouvez utiliser des liaisons .
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();
Habituellement, vous utilisez des instructions préparées lorsque vous souhaitez répéter rapidement quelque chose (comme un INSERT) plusieurs fois. L'instruction préparée fait en sorte que l'instruction SQL n'ait pas à être analysée et compilée à chaque fois. Vous pouvez accélérer les choses encore plus en utilisant des transactions . Cela permet d'appliquer toutes les modifications en même temps. Voici un exemple:
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();
}
Consultez ces liens pour plus d'informations sur les transactions et l'accélération des insertions de bases de données.
Ceci est un exemple basique. Vous pouvez également appliquer les concepts de la section ci-dessus.
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();
La executeUpdateDelete()
méthode peut également être utilisée pour les instructions DELETE et a été introduite dans l'API 11. Voir cette Q&R .
Voici un exemple.
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();
}
Normalement, lorsque vous exécutez une requête, vous souhaitez récupérer un curseur avec beaucoup de lignes. Ce n'est pas pour ça SQLiteStatement
, cependant. Vous n'exécutez pas de requête avec lui, sauf si vous n'avez besoin que d'un résultat simple, comme le nombre de lignes dans la base de données, que vous pouvez faire avecsimpleQueryForLong()
String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();
Habituellement, vous exécuterez la query()
méthode de SQLiteDatabase pour obtenir un curseur.
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);
Consultez cette réponse pour plus de détails sur les requêtes.
clearBindings()
ne les définissent pas seulement sur null
(voir le code source ). Je vois cela comme un effacement de l'état afin que rien ne l'influençât de la boucle précédente. Ce n'est peut-être pas nécessaire, cependant. Je serais heureux que quelqu'un sache commenter.
Si vous voulez un curseur au retour, vous pouvez envisager quelque chose comme ceci:
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"));
Voir cet extrait de Genscripts au cas où vous en voudriez un plus complet. Notez qu'il s'agit d'une requête SQL paramétrée, c'est donc essentiellement une instruction préparée.
L'exemple de jasonhudgins ne fonctionnera pas. Vous ne pouvez pas exécuter une requête avec stmt.execute()
et récupérer une valeur (ou a Cursor
).
Vous ne pouvez précompiler que les instructions qui ne retournent aucune ligne (comme une instruction insert ou create table) ou une seule ligne et colonne (et utilisez simpleQueryForLong()
ou simpleQueryForString()
).