web-dev-qa-db-ja.com

AndroidのSQliteで準備済みステートメントを使用するにはどうすればよいですか?

AndroidのSQliteで準備済みステートメントを使用するにはどうすればよいですか?

97
pupeno

私はAndroidで準備されたステートメントを常に使用します、それは非常に簡単です:

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

Androidがあります SQLiteStatement 。SQLiteStatement(---)で準備されたSQLiteステートメントの場合。また、インジェクション攻撃の防止にも役立ちます。準備済みステートメントに関する一般的な説明については、 この記事を参照してください

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、およびこれらの戻り値のため、更新。 (ただし、 この質問 を参照してください。)

値を挿入

_String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();
_

executeInsert()ではなく、 execute() メソッドが使用されることに注意してください。もちろん、すべての行に同じものを常に入力する必要はありません。そのために 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();
_

通常、何か(INSERTなど)をすばやく何度も繰り返す場合は、準備済みステートメントを使用します。準備されたステートメントは、SQLステートメントを毎回解析およびコンパイルする必要がないようにします。 transactions を使用すると、さらに高速化できます。これにより、すべての変更を一度に適用できます。以下に例を示します。

_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で導入されました。 このQ&A を参照してください。

以下に例を示します。

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

通常、カーソルを取得するには、 SQLiteDatabasequery()メソッドを実行します。

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

クエリの詳細については、 この回答 を参照してください。

155
Suragch

戻り時にカーソルが必要な場合は、次のようなものを検討できます。

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クエリであるため、本質的には準備されたステートメントであることに注意してください。

21
jbaez

jasonhudginsの例は機能しません。 stmt.execute()を使用してクエリを実行し、値(またはCursor)を取得することはできません。

行をまったく返さないステートメント(挿入、テーブル作成ステートメントなど)、または単一の行と列(およびsimpleQueryForLong()またはsimpleQueryForString()を使用)のみをプリコンパイルできます。

9
redfish64

カーソルを取得するには、compiledStatementを使用できません。ただし、完全に準備されたSQLステートメントを使用する場合は、jbaezのメソッドを適応させることをお勧めします...ではなくdb.rawQuery()db.query()

1
Aaron