SQLiteの操作

Androidに標準で組み込まれているSQLiteというデータベースを、Androidが用意しているAPIを使用して、アプリケーションから利用する方法を示します。

■はじめに・・・

 SQLiteはアプリケーションローカルで操作できるデータベースで、以下の用途に使用できます。
 
  参考:@IT「AndroidでSQLiteのDB操作をするための基礎知識」http://www.atmarkit.co.jp/fjava/rensai4/android06/android06_1.html

  1.データを次々に蓄積して、その中からデータを抽出するタイプのアプリケーション
   「単語帳」「メッセンジャー」など

  2.データを永続的に保持しておくタイプのアプリケーション
   「単語帳」「メッセンジャー」「通信ログ」など

  3.大量のデータから任意のデータを探すタイプのアプリケーション
   「辞書」「データベースアプリ」など

 ●SQLite

  参考:「フリー百科事典『ウィキペディア(Wikipedia)』」http://ja.wikipedia.org/wiki/SQLite

  ・サーバとしてではなくアプリケーションに組み込まれて利用される軽量のデータベースである。

  ・RDBMSに比べて大規模な仕事には不向きだが、中小規模ならば速度に遜色はない。

  ・APIは単純にライブラリを呼び出すだけである。

  ・データの保存に単一のファイルのみを使用する。

  ・バージョン3.3.8からは全文検索のFTS1モジュールがサポートされた。


■ サンプルアプリ概要

 参考:@IT「AndroidでSQLiteのDB操作をするための基礎知識」http://www.atmarkit.co.jp/fjava/rensai4/android06/android06_1.html

 コンボの都道府県を選択すると、県庁所在地と郷土料理(複数)を表示します。

 サンプル画面 サンプル画面(凡例).JPG 


 スキーマ 

  スキーマ.jpg 

 ■処理手順

 (1)ActivityのonCreate()で、SQLiteデータベース管理を行うandroid.database.sqliteパッケージ(http://developer.android.com/intl/ja/reference/android/database/sqlite/package-summary.html)を使用し、データベースを構築します。

 (2)内部クラスにDatabaseHelperをSQLiteOpenHelper(http://developer.android.com/intl/ja/reference/android/database/sqlite/SQLiteOpenHelper.html)の継承クラスとして定義し、superのコンストラクタでDBを構築します。
  ・コンストラクタの第2引数がnullの場合、メモリ上にデータベースを構築します。任意の文字列でファイル名(pathは指定不可)を指定した場合、/data/data/<パッケージ名>/database/<ファイル名>で作成されます。
  
 (3)SQLiteOpenHelperのコンストラクタでデータベースファイルが新規の場合、内部クラスDatabaseHelperのonCreate()がコールされます。本サンプルでは、このときに初期データの投入を行っています。
  ・既存のデータベースファイルの有無により、onCreate()がコールされるか?が決まります。エミュレータでの実施結果では、データベースファイルの作成方法(オンメモリ/任意ファイル名指定)により結果が異なりました。
   オンメモリ  アプリを起動すると、毎回、onCreate()がコールされる。
       →毎回、データベースファイルが揮発しているような動作をする。
   任意のファイル アプリを起動すると、ファイルが存在しない場合のみonCreate()がコールされる。
       →データベースファイルは揮発していない。ただし、本サンプルのようにonCreate()で初期データの投入を行っている場合、アプリをアンインストールまたは、直接コマンドでデータベースファイルを削除しなければ、onCreate()がコールされず、初期データの内容を変更してもonCreate()がコールされないので、データベースに反映されない。

 (4)beginTransaction()で、トランザクションを開始します。

 (5)execSQL()で、県庁所在地テーブル(capitals)を作成します。
  SQL:create table capitals (prefecture text primary key, capital text not null);
 
 (6)再利用可能なSQLステートメントを事前にコンパイルします。
  SQL:insert into capitals values (?, ?);
  ※?は、実際の設定値を紐つけることを意味します。(後述、⑧)
 
 (7)executeInsert()で、内部配列データ(CAPITALS)を県庁所在地テーブルに一括追加する処理を定義します。
 
 (8)コンパイル済みのSQLステートメントに値を紐付けます。
  コンパイル済みのSQLステートメントに"?"が複数存在する場合、定義順にbindStringの第一引数(1,2,3...)に対応します。
 
 (9)コンパイル済みのSQLステートメントで、INSERT文を実行します。
 
 (10)setTransactionSuccessful()で、トランザクションに成功のマークを付与します。
 
 (11)endTransaction()で、トランザクションを終了します。
  成功マークが付与されているものはコミット、それ以外はロールバックされます。
 
 (12)データベースをバージョンアップしたときにコールされる関数で、本サンプルでは未使用です。
  通常、現行バージョン番号→新バージョン番号のコンバート内容を定義します。
  因みに現行バージョン番号、新バージョン番号の大小関係とは無関係に、変化があったときは必ずコールされます。

 (13)getReadableDatabase()で作成またはオープンされたDBのオブジェクトを取得します。
 
 (14)県庁所在地テーブルから都道府県名を抽出します。
  引数の内容は、"SELECT prefecture FROM capitals"と同じ。

 (15)カーソルの先頭にポインタを移動します。
 
 (16)県庁所在地テーブルの取得データ数分、都道府県名配列を作成します。
 
 (17)カーソルからデータを取得し、都道府県名配列にセットします。
 
 (18)カーソルを次のポインタに移動します。
 
 (19)カーソルをクローズします。
 
 (20)コンボボックスに都道府県名をセットします。
 
 (21)コンボボックスが選択されたときに呼び出されるコールバック関数を定義します。
 
 (22)コンボボックスで選択された都道府県名を取得します。
 
 (23)県庁所在地テーブルを都道府県名で検索し、県庁所在地を取得します。
  引数の内容は、SELECT capital FROM capitals WHERE prefecture='<都道府県名>'と同じ。

 (24)県庁所在地を取得します。
 
 (25)カーソルをクローズします。
 
 (26)テキストビュー(TextView01)に県庁所在地をセットします。
 
 (27)郷土料理テーブルを都道府県名で検索し、郷土料理名を取得します。(複数件)
 
 (28)郷土料理テーブルの取得データ数分、郷土料理配列を作成します。
 
 (29)カーソルからデータを取得し、郷土料理配列にセットします。
 
 (30)カーソルを次のポインタに移動します。
 
 (31)カーソルをクローズします。


■コード例

    package com.example.android.db;

    import android.app.Activity;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    import android.database.sqlite.SQLiteStatement;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.ArrayAdapter;
    import android.widget.ListView;
    import android.widget.Spinner;
    import android.widget.TextView;
    import android.widget.AdapterView.OnItemSelectedListener;

    public class Main extends Activity {

        private DatabaseHelper helper;
        protected static final String TAG = "Main";

        @Override
        public void onCreate(Bundle savedInstanceState) {
            Log.d(TAG, "### onCreate Start ###");
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

            helper = new DatabaseHelper(this);                              ←(1)
            final SQLiteDatabase db = helper.getReadableDatabase();         ←(13)

            Cursor c = db.query("capitals", new String[] {"prefecture"}, null, null, null, null, null); ←(14)
            c.moveToFirst();                                                ←(15)

            CharSequence[] list = new CharSequence[c.getCount()];           ←(16)
            for (int i = 0; i < list.length; i++) {
                list[i] = c.getString(0);                                   ←(17)
                c.moveToNext();                                             ←(18)
            }
            c.close();                                                      ←(19)

            Spinner spinner = (Spinner)findViewById(R.id.Spinner01);
            spinner.setAdapter(new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item, list));   ←(20)
            spinner.setOnItemSelectedListener(new OnItemSelectedListener() {            ←(21)

                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                    Log.d(TAG, "### onItemSelected Start ###");
                    String prefecture = ((Spinner)parent).getSelectedItem().toString(); ←(22)
                    Cursor c;

                    c = db.query("capitals", new String[] {"capital"}, "prefecture='" + prefecture + "'", null, null, null, null);  ←(23)
                    c.moveToFirst();
                    String capital = c.getString(0);                                    ←(24)
                    c.close();                                                          ←(25)

                    TextView textView = (TextView)findViewById(R.id.TextView01);
                    textView.setText(capital);                                          ←(26)

                    c = db.query("local_dishes", new String[] {"local_dish"}, "prefecture='" + prefecture + "'", null, null, null, null);   ←(27)
                    c.moveToFirst();
                    CharSequence[] list = new CharSequence[c.getCount()];               ←(28)
                    for (int i = 0; i < list.length; i++) {
                        list[i] = c.getString(0);                                       ←(29)
                        c.moveToNext();                                                 ←(30)
                    }
                    c.close();                                                          ←(31)
                    ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(Main.this, android.R.layout.simple_list_item_1, list);
                    ListView listView = (ListView)findViewById(R.id.ListView01);
                    listView.setAdapter(adapter);
                    Log.d(TAG, "### onItemSelected Ended ###");
                }

                @Override
                public void onNothingSelected(AdapterView<?> parent) {
                    Log.d(TAG, "### onNothingSelected Start ###");
                    Log.d(TAG, "### onNothingSelected Ended ###");
                }

            });
            Log.d(TAG, "### onCreate Ended ###");

        }

        @Override
        protected void onDestroy() {
            Log.d(TAG, "### onDestroy Start ###");
            super.onDestroy();
            helper.close();
            Log.d(TAG, "### onDestroy Ended ###");
        }

        class DatabaseHelper extends SQLiteOpenHelper {

            protected static final String TAG = "DatabaseHelper";

            private String[][] CAPITALS = {
                {"北海道", "札幌"},
                {"青森県", "青森"},
                ・・・
                {"沖縄県", "那覇"},
            };

            private String[][] LOCAL_DISHES = {
                {"北海道", "いかそうめん"},
                {"北海道", "いかのごろ煮"},
                {"北海道", "いももち"},
                {"北海道", "イカ飯"},
                {"北海道", "エスカロップ"},
                {"北海道", "ジンギスカン"},
                {"北海道", "チャンチャン焼き"},
                {"北海道", "チーズ入りいも団子"},
                {"北海道", "ルイベ"},
                {"北海道", "石狩鍋"},
                {"北海道", "豚丼"},
                ・・・
                {"沖縄県", "ちんびん"},
                {"沖縄県", "にんじんしりしり"},
                {"沖縄県", "ククメシ"},
                {"沖縄県", "ゴーヤチャンプルー"},
                {"沖縄県", "ソーミンチャンプルー"},
                {"沖縄県", "ヒラヤーチ"},
                {"沖縄県", "ミヌダル"},
            };

            public DatabaseHelper(Context context) {
                super(context, null, null, 1);                          ←(2)
    //          super(context, "DB_Demo", null, 1);
            }

            @Override
            public void onCreate(SQLiteDatabase db) {                   ←(3)
                Log.d(TAG, "### onCreate Start ###");
                db.beginTransaction();                                  ←(4)
                try {
                    SQLiteStatement stmt;
                    db.execSQL("create table capitals (prefecture text primary key, capital text not null);");          ←(5)
                    stmt = db.compileStatement("insert into capitals values (?, ?);");                                  ←(6)

                    for (String[] capital : CAPITALS) {                 ←(7)
                        stmt.bindString(1, capital[0]);                 ←(8)
                        stmt.bindString(2, capital[1]);
                        stmt.executeInsert();                           ←(9)
                    }

                    db.execSQL("create table local_dishes (prefecture text not null, local_dish text not null);");
                    stmt = db.compileStatement("insert into local_dishes values (?, ?);");

                    for (String[] localDish : LOCAL_DISHES) {
                        stmt.bindString(1, localDish[0]);
                        stmt.bindString(2, localDish[1]);
                        stmt.executeInsert();
                    }
                    db.setTransactionSuccessful();                      ←(10)
                } finally {
                    db.endTransaction();                                ←(11)
                }
                Log.d(TAG, "### onCreate Ended ###");
            }

            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {      ←(12)
                Log.d(TAG, "### onUpgrade Start ###");
                Log.d(TAG, "# oldVersion[" + oldVersion + "]");
                Log.d(TAG, "# newVersion[" + newVersion + "]");
                Log.d(TAG, "### onUpgrade Ended ###");
            }

        }

    }