コンピュータクワガタ

かっぱのかっぱによるコンピュータ関連のサイトです

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は「'」などがエスケープされるため、入力された条件のままで検索される。そのため検索条件には一致しない。
ただし、%はエスケープされないので、すべての検索になる。