2013年6月21日金曜日

[Android]SQLiteによるカーソルのループ処理の書き方

AndroidでSQLiteからデータを参照する場合、以下のような手順で処理を実装します。
  • SQLiteOpenHelperを生成する
  • SQLiteOpenHelperから、SQLiteDatabaseを取得する(SQLiteOpenHelper#getReadableDatabase()など)
  • SQLiteDatabaseを使ってクSQLを実行する(SQLiteOpenHelper#rawQuery()など)
  • Cursorを使ってデータを取り出す
という手順が一般的だと思います。
上記以外にSQLiteOpenHelperの継承クラスを用意し、DB及びテーブルを準備する実装が別途必要になります。まずは、そのコード例です。

class DBOpenHelper extends SQLiteOpenHelper {

    public DBOpenHelper(Context context) {
        super(context, "test.db"null1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // テーブルの生成
        db.execSQL("CREATE TABLE TEST_TABLE(id INTEGER PRIMARY KEY, memo TEXT NOT NULL, latitude REAL, longitude REAL);");

        // 初期データの投入
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge1')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge2')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge3')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge4')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge5')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge6')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge7')");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // nop
    }
}


で肝心のループ処理。Cursor#getCount()を使えば、件数を取得できます。カーソルの処理において、件数を使ってループさせるのはあまりかっこよくない(個人的に)ので、その方式は除外します。
public void onButton2(View view) {

    // DBをオープンして、簡単なクエリーを投げる
    SQLiteOpenHelper helper = new DBOpenHelper(this);
    SQLiteDatabase db = helper.getReadableDatabase();
    Cursor cursor = db
            .rawQuery("select * from TEST_TABLE"new String[] {});

    // 方式1:forを使ったループ
    for (boolean next = cursor.moveToFirst(); next; next = cursor
            .moveToNext()) {
        // ・・・処理
        Log.d("sql""code1 data=" + cursor.getString(1));
    }

    // 方式2:whileを使ったループ
    boolean next = cursor.moveToFirst();
    while (next) {
        // ・・・処理
        Log.d("sql""code2 data=" + cursor.getString(1));

        // 次のレコード
        next = cursor.moveToNext();
    }

    cursor.close();
    db.close();
    helper.close();

}

「方式1:forを使ったループ」は、Effective Javaの項目45の、ローカル変数のスコープを最小化するの方式を使った例です。
この方式では、nextというローカル変数がfor文内部だけのスコープになるので、いくつかのクエリーを実行する場合、ミスは少なくなると思いますが、for文はカウントアップするという感覚があるので(個人的に)、なんか可読性が低い気がします。

「方式2:whileを使ったループ」では、ローカル変数のスコープが広くなるという欠点と、cursor#moveToNext()をループの最後に残し続けなければならないという制約が残ります。そのため修正を行う際、事故が起こりそうな感じはありますが、(個人的に)可読性が良いと考えています。

どちらもビミョーですが、可読性の観点から「方式2:whileを使ったループ」のほうが良いと考えています。

そもそもCursor#hasNext()的なメソッドがあればこんなことにはならなかったのではないかと思います。そうすれば、while (cursor.hasNext())一択になるのに。 なんでないんだろう。。。

0 件のコメント: