Androidアプリ入門 No.74 SQLiteの使用 安全な登録をする
SQLiteの使用
安全な登録をする
先の例では、INSERT文に入力値をそのまま渡していた。そのためSQLインジェクションの恐れがある。Webアプリと違い、ローカルのアプリケーションとなるため実際には問題にならないケースが多かもしれない(不具合にはなる)が不正な入力はできないほうがよい。そのため、JDBCのPreparedStatementのような仕組みがAndroidにもある。先の例のDbTest.javaを以下のようにする。
package sample.dt; import android.app.Activity; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteStatement; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class DbTest extends Activity { private EditText searchEditText; private Button readButton; private TextView resultTextView; private EditText nameEditText; private EditText ageEditText; private Button insertButton; private DatabaseHelper databaseHelper; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // DB作成 databaseHelper = new DatabaseHelper(getApplicationContext()); searchEditText = (EditText) findViewById(R.id.searchEditText); readButton = (Button) findViewById(R.id.readButton); readButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { read(); } }); resultTextView = (TextView) findViewById(R.id.resultTextView); nameEditText = (EditText) findViewById(R.id.nameEditText); ageEditText = (EditText) findViewById(R.id.ageEditText); insertButton = (Button) findViewById(R.id.insertButton); insertButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { insert(); } }); } private void read() { String search = searchEditText.getText().toString(); if (search.equals("")) { Toast.makeText(getApplicationContext(), "検索条件を入力してください。", Toast.LENGTH_SHORT).show(); return; } SQLiteDatabase db = databaseHelper.getReadableDatabase(); Cursor cursor = db.query("emp", new String[] { "name", "age" }, "name LIKE '%' || ? || '%'", new String[] { search }, null, null, "age DESC"); cursor.moveToFirst(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < cursor.getCount(); i++) { sb.append(cursor.getString(0)).append(","); sb.append(cursor.getInt(1)).append("\n"); cursor.moveToNext(); } cursor.close(); resultTextView.setText(sb.toString()); } private void insert() { String name = nameEditText.getText().toString(); if (name.equals("")) { Toast.makeText(getApplicationContext(), "登録条件(名前)を入力してください。", Toast.LENGTH_SHORT).show(); return; } String age = ageEditText.getText().toString(); if (age.equals("")) { Toast.makeText(getApplicationContext(), "登録条件(年齢)を入力してください。", Toast.LENGTH_SHORT).show(); return; } SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteStatement stmt = db.compileStatement("INSERT INTO emp(name, age) VALUES(?, ?)"); stmt.bindString(1, name); stmt.bindLong(2, Long.parseLong(age)); stmt.executeInsert(); nameEditText.setText(""); ageEditText.setText(""); Toast.makeText(getApplicationContext(), "データを登録しました。", Toast.LENGTH_SHORT).show(); } }
実行結果は以下のようになる。「'」を入力しても問題なく動作する。
DBへのINSERTの部分は以下のようになっている。
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteStatement stmt = db.compileStatement("INSERT INTO emp(name, age) VALUES(?, ?)"); stmt.bindString(1, name); stmt.bindLong(2, Long.parseLong(age)); stmt.executeInsert();
ここで使用している、SQLiteDatabase#compileStatementがJDBCのPreparedStatementに近いものとなる。具体的なメソッド定義は以下のようになる。
public SQLiteStatement compileStatement(String sql)
引数の詳細は以下。
引数 | 説明 |
---|---|
sql | SQL。?が置き換え文字となっている。 |
上記の例で、SQLの中で「VALUES(?, ?)」と実際に登録する値が置き換え文字列となっている。この?に値を格納するには、SQLiteDatabase#compileStatementの戻り値であるSQLiteStatementのbindXxxメソッドを使用する。bindXxxメソッドには以下の種類がある。indexは1から始まり、適切にエスケープされる。
メソッド | 説明 |
---|---|
bindNull(int index) | indexの場所をnullで置き換える。 |
bindLong(int index, long value) | indexの場所をvalueで置き換える。 |
bindDouble(int index, long doube) | indexの場所をvalueで置き換える。 |
bindString(int index, String value) | indexの場所をvalueで置き換える。 |
bindBlob(int index, byte[] value) | indexの場所をvalueで置き換える。 |
最後に、SQLiteStatement#executeInsertメソッドを確認する。定義は以下となる。
public long executeInsert()
引数はなく、INSERT文が実行される。executeメソッドだとINSERTだけでなく、UPDATE、DELETEも実行できる。executeInsertを使用すると戻り値で自動生成されたシーケンス値が取得できる。ログにキー項目を出力したり、同一キーで関連テーブルを更新する場合等に使用できる。