■はじめに・・・
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
コンボの都道府県を選択すると、県庁所在地と郷土料理(複数)を表示します。
サンプル画面
スキーマ
■処理手順
(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 ###");
}
}
}
コメントする