web-dev-qa-db-fra.com

Comment utiliser des instructions préparées dans SQlite sous Android?

Comment utiliser des instructions préparées dans SQlite sous Android?

97
pupeno

J'utilise des instructions préparées dans Android tout le temps, c'est assez simple:

SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("SELECT * FROM Country WHERE code = ?");
stmt.bindString(1, "US");
stmt.execute();
24
jasonhudgins

Pour les instructions SQLite préparées dans Android, il existe SQLiteStatement . Les instructions préparées vous aident à améliorer les performances (en particulier pour les instructions devant être exécutées plusieurs fois) et vous aident également à éviter attaques par injection.Voir 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 utiliseriez pas pour la plupart des requêtes.) Voici quelques exemples:

Créer une table

String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();

La méthode execute() ne renvoie pas de valeur. Il est donc approprié de l'utiliser avec CREATE et DROP, mais elle n'est pas destinée à être utilisée avec SELECT, INSERT, DELETE et UPDATE, car ces valeurs de retour . (Mais voir cette question .)

Insérer des valeurs

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 méthode executeInsert() est utilisée à la place de execute(). Bien sûr, vous ne voudriez pas toujours entrer les mêmes choses dans chaque ligne. Pour cela, vous pouvez utiliser bindings .

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();

En général, vous utilisez des instructions préparées lorsque vous souhaitez répéter rapidement plusieurs opérations (comme une instruction INSERT). L'instruction préparée fait en sorte que l'instruction SQL ne doit pas nécessairement être analysée et compilée à chaque fois. Vous pouvez accélérer encore plus les choses en utilisant transactions . Cela permet d'appliquer tous les changements 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 en savoir plus sur les transactions et sur l’accélération des insertions dans la base de données.

Mise à jour des lignes

Ceci est un exemple de base. 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();

Supprimer les lignes

La méthode executeUpdateDelete() peut également être utilisée pour les instructions DELETE et a été introduite dans l'API 11. Voir cette Q & A .

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();
}

Requête

Normalement, lorsque vous exécutez une requête, vous souhaitez obtenir un curseur contenant beaucoup de lignes. Ce n’est pas pour cela que SQLiteStatement, cependant. Vous n'exécutez pas de requête avec cette dernière sauf si vous n'avez besoin que d'un résultat simple, comme le nombre de lignes de la base de données, que vous pouvez utiliser avec simpleQueryForLong()

String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();

Généralement, vous exécuterez la méthode query() 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);

Voir cette réponse pour plus de détails sur les requêtes.

155
Suragch

Si vous voulez un curseur sur le retour, alors vous pourriez 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 Genscripts au cas où vous en voudriez un plus complet. Notez qu'il s'agit d'une requête SQL paramétrée. Il s'agit donc essentiellement d'une instruction préparée.

21
jbaez

exemple jasonhudgins ne fonctionnera pas. Vous ne pouvez pas exécuter une requête avec stmt.execute() et obtenir une valeur (ou un Cursor) en retour.

Vous pouvez uniquement précompiler des instructions qui ne renvoient aucune ligne (comme une instruction insert ou create table) ou une seule ligne et une seule colonne (et utilisez simpleQueryForLong() ou simpleQueryForString()).

9
redfish64

Pour obtenir un curseur, vous ne pouvez pas utiliser un état compilé. Toutefois, si vous souhaitez utiliser une instruction SQL préparée complète, je vous recommande d'adapter la méthode de jbaez ... Utilisation de db.rawQuery() au lieu de db.query().

1
Aaron