AndroidのSQliteで準備済みステートメントを使用するにはどうすればよいですか?
AndroidのSQliteで準備済みステートメントを使用するにはどうすればよいですか?
回答:
私は常にAndroidでプリペアドステートメントを使用しています。これは非常に簡単です。
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();
SQLiteStatement.bindXXX()
は1ベースのインデックスを持っていることに注意してください。ほとんどのインデックスのように0ベースではありません。
Androidで準備されたSQLiteステートメントにはSQLiteStatementがあります。準備されたステートメントは、パフォーマンスを高速化するのに役立ち(特に、複数回実行する必要があるステートメントの場合)、インジェクション攻撃を回避するのにも役立ちます。準備されたステートメントに関する一般的な議論については、この記事を参照してください。
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、およびUPDATEでの使用は意図されていません。(しかし、この質問を参照してください。)
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();
executeInsert()
メソッドではなくが使用されていることに注意してくださいexecute()
。もちろん、すべての行に常に同じものを入力する必要はありません。そのためには、バインディングを使用できます。
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ステートメントを毎回解析してコンパイルする必要がないようにします。トランザクションを使用すると、さらにスピードを上げることができます。これにより、すべての変更を一度に適用できます。次に例を示します。
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();
通常query()
、SQLiteDatabaseのメソッドを実行してカーソルを取得します。
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);
クエリの詳細については、この回答を参照してください。
clearBindings()
はそれらを単に設定するだけではありませんnull
(ソースコードを参照)。私はそれを状態をクリアするものと見なし、前のループからの影響は何もないようにします。たぶんそれは必要ないかもしれません。コメントを知っている人がいてくれたら嬉しいです。
戻り時にカーソルが必要な場合は、次のようなものを検討できます。
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クエリであるため、基本的には準備されたステートメントです。