Androidアプリ入門 No.70 SQLiteの使用 query
すでに入門ではなくなってきているような気もするが、入門。No. 99までには終わる予定。
サービスがいまいちうまい例ができない。だから、SQLiteに逃亡してるところです。
SQLiteの使用
query
先ほどの例で簡単なqueryによりテーブルからデータを取得したが、通常のSELECT文を発行してデータを取得することもできる。先ほどの例のDbTest.javaを以下のように変更する。
package sample.dt; import android.app.Activity; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class DbTest extends Activity { private Button readButton; private TextView resultTextView; private DatabaseHelper databaseHelper; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // DB作成 databaseHelper = new DatabaseHelper(getApplicationContext()); readButton = (Button) findViewById(R.id.readButton); readButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { read(); } }); resultTextView = (TextView) findViewById(R.id.resultTextView); } private void read() { SQLiteDatabase db = databaseHelper.getReadableDatabase(); String sql = "SELECT name,age FROM emp ORDER BY age DESC"; Cursor cursor = db.rawQuery(sql, null); // Cursor cursor = db.query("emp", new String[] { "name", "age" }, null, null, null, null, // null); 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()); } }
実行結果は先ほどと同様。ORDERを付けているためデータの並びは変わっている。この例を少し変えて検索条件を加えられるようにする。readメソッドを以下のように変更する。
private void read() { SQLiteDatabase db = databaseHelper.getReadableDatabase(); String sql = "SELECT name,age FROM emp WHERE name LIKE '%' || ? || '%' ORDER BY age DESC"; Cursor cursor = db.rawQuery(sql, new String[] { "ニ" }); 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()); }
実行結果は以下のようになる。nameに「ニ」を含むレコードの一覧が表示される。
SQLiteDatabase#rawQueryメソッドの定義は以下のようになっている。
public Cursor rawQuery (String sql, String[] selectionArgs)
引数の詳細は以下。
引数 | 説明 |
---|---|
sql | SQL。 |
selectionArgs | 置き換え文字。 |
先ほどの例では、以下のように使用している。
String sql = "SELECT name,age FROM emp WHERE name LIKE '%' || ? || '%' ORDER BY age DESC"; Cursor cursor = db.rawQuery(sql, new String[] { "ニ" });
最初の?に「ニ」がマッピングされて、LIKE '%ニ%'で検索される。この?での置き換えは文字のエスケープも行われるため、ユーザー入力をSQLに含める場合には基本的には置き換え文字列で行うべきである。以下、ユーザー入力で検索する例を示す。まず、main.xmlを以下のようにする。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <EditText android:id="@+id/searchEditText" android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="検索条件" /> <Button android:id="@+id/readButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="データ読み込み" /> <TextView android:id="@+id/resultTextView" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
次に、DbTest.javaを以下のように変更する。具体的には、EditTextを新しく設け、そこに入力された文字列でnameを部分一致検索している。
package sample.dt; import android.app.Activity; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; 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 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); } private void read() { String search = searchEditText.getText().toString(); if (search.equals("")) { Toast.makeText(getApplicationContext(), "検索条件を入力してください。", Toast.LENGTH_SHORT).show(); return; } SQLiteDatabase db = databaseHelper.getReadableDatabase(); String sql = "SELECT name,age FROM emp WHERE name LIKE '%' || ? || '%' ORDER BY age DESC"; Cursor cursor = db.rawQuery(sql, new String[] { search }); 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()); } }
悪いことをしようとしても、その文字列での検索になっているため、検索できない。
この検索条件を単純にSQLに割り当てると以下のようになる。
SELECT name,age FROM emp WHERE name LIKE '%' || '' OR 1=1 --' || '%' ORDER BY age DESC
つまり、検索条件に「OR 1=1」があるためすべてのデータを取得してしまうような条件になる。しかし、?でバインドされたSQLは「'」などがエスケープされるため、入力された条件のままで検索される。そのため検索条件には一致しない。
ただし、%はエスケープされないので、すべての検索になる。