世界を目指せ!Androidアプリ開発入門

第8回AndroidのSQLiteを学ぶ

今回は、Android OSに搭載されているRDBMS「SQLite」について解説します。

前回のおさらい

前回は、Android OSのユーザインターフェースのパーツの使い方を学びました。

具体的には、

  • Androidのレイアウトシステム
  • リストビューの使い方

の説明をしました。

物足りないと感じた方は、ぜひともサンプルプログラムに手を加えて、イメージするユーザインターフェースを持つアプリを構築する時間を作って下さい。少ない手掛かりで進めるのは、回り道にも感じますが、試行錯誤して身に付けた知識やノウハウは、問題解決の際や新しい切り口を検討する際の糧となるはずで、決して無駄にはならず、回り道にもなりません。

SQLiteとは?

今回は、Android OSに搭載されているリレーショナルデータベースシステム「SQLite」を取り上げます。

SQLiteをご存じではない方のために、簡単にご紹介します。SQLiteは、データ保存に単一ファイルを使用する、クロスプラットホームの軽量リレーショナルデータベースシステムです。一般的なデータベースサーバーとは異なり、アプリに組み込まれて使われます。データベースの操作は、プロトコルやプロセス間通信を使わず、APIを呼び出すので高速に動作するのところが特徴です。

SQLiteで扱えるデータ型は、以下の5種類で非常にシンプルです。

INTEGER符号付整数
REAL浮動小数点
TEXTテキスト
BLOBバイナリデータ
NULLNULL

一般的なデータベースでは、カラムごとにデータ型を指定し格納できる値が決められていますが、SQLiteでは、テーブルを作成するときに、データ型を指定しなくても良い寛大な設計になっているところも特徴的な部分です。

それでは、SQLiteの使い方をご紹介します。

今回も例として上げているコードは、サンプルプログラムとして以下からダウンロードできます。

実行画面
画像

ヘルパークラスの実装

SQLiteOpenHelperというヘルパークラスを継承してDBOpenHelperクラスを作成します。

SQLiteOpenHelperクラスのコンストラクタには、第一引数にContext、第二引数にデータベース名、第三引数にCursorFactory、第四引数にデータベースのバージョン番号といった値が渡します。

public DBOpenHelper( Context context )
{
	super( context, DB_NAME, null, DB_VERSION );
}

テーブルの作成

まずは、テーブルを作成します。

SQLiteOpenHelperクラスは、オブジェクトが生成されるとデータベースが存在するか確認され存在しない場合は、onCreateメソッドが呼び出されます。このタイミングで、以下のようなテーブルを作成するクエリーを実行します。

@Override
public void onCreate(SQLiteDatabase db) {
	db.execSQL(
			"CREATE TABLE IF NOT EXISTS Test ( _id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , caption TEXT )"
			);		
}

アップグレード処理

SQLiteOpenHelperクラスには、データベースをアップグレードする仕組みがあります。

SQLiteOpenHelperクラスのオブジェクトが生成されると、スキーマのバージョンのチェックが行われ、バージョンが変更されているとonUpgradeメソッドが呼び出されます。

たとえば、バージョン1のデータベースに、新しいフィールドを追加して、バージョン2とする場合は、以下のようなコードになります。

public DBOpenHelper( Context context )
{
	// バージョンを 2 にする
	super( context, DB_NAME, null, 2 );
}

スーパークラスのコンストラクタの第三引数は、バージョンなので2に設定します。

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
	if( oldVersion == 1 )
	{
		// アップグレードコード
		db.execSQL( "ALTER TABLE Test add add_text TEXT" );
	}
}

バージョンが1から2に変更されたので、onUpgradeメソッドが呼び出されます。

onUpgradeメソッドの第二引数には、古いバージョン番号が入っているので、これをチェックしてテーブルのアップグレードコードを実行します。

onUpgradeメソッドは、ユーザの手を煩わせることなくデータベースをアップグレードできるので、便利な仕組みですが使用には注意が必要です。

先でご説明したように、onUpgradeメソッドが実行されるのは、オブジェクトが生成されるタイミングです。この時に、多くのレコードが保存されているテーブルをまるごと変換するような時間のかかる処理を行うと、Android OSが応答しないアプリと判断して強制終了の確認ダイアログを表示します。onUpgradeメソッドで、時間がかかる処理を実行する時は、onUpgradeメソッドでフラグだけ立てて、別の機会に処理を行うなどの工夫が必要になります。

レコードの追加

テーブルへのレコード追加は、以下のようなコードになります。

public void add( String caption ) {
	ContentValues val = new ContentValues();

	val.put( "caption", caption );
	m_db.insert( TBL_NAME, null, val );
}

レコードの追加には、ContentValuesクラスを使います。

ContentValuesクラスは、テーブルに含まれるカラムをキーとし、カラムに対して設定したい値をペアとして保存するためのクラスです。ContentValuesクラスのputメソッドの第一引数にカラム名を表すStringクラスの値を指定します。そして、第二引数にカラムに設定されているデータ型に対応したクラスの値を指定します。

m_dbは、SQLiteOpenHelperのgetWritableDatabaseメソッドで取得したSQLiteDatabaseクラスのハンドルです。SQLiteDatabaseクラスのinsertメソッドの第三引数にContentValuesを渡してレコードを追加します。

レコードの更新

レコードの更新は、レコード追加と似たコードで、以下のようになります。

public void update( int id, String caption ) {
	ContentValues val = new ContentValues();

	val.put( "caption", caption );
	m_db.update( TBL_NAME, val, "_id=?", new String[] { Integer.toString( id ) });		
}

追加と同様に、ContentValuesクラスのputメソッドの第一引数にカラム名を表すStringクラスの値を指定します。そして、第二引数にはカラムに設定されているデータ型に対応したクラスの値を指定します。

SQLiteDatabaseクラスのupdateメソッドでレコードの更新を行います。

updateメソッドは、第二引数で設定した値を、第三引数で指定した条件に一致するレコードを更新します。第四引数では、第三引数で指定する条件の引数を指定します。引数の変数は「?」で指定します。

レコードの読み出し

レコードの読み出しは、以下のようなコードになります。

public String[] loadAll()
{
	int						i;
	Cursor					c;
	String[]		entries;

	if( m_db == null )
       	return null;

	c = m_db.query( TBL_NAME,
					new String[] { "_id", "caption" },
					null, null, null, null, null );
	
	int numRows = c.getCount();

	c.moveToFirst();

	entries = new String[numRows];
		for( i = 0; i < numRows; i++ )
		{
			entries[i] = c.getString(1);
			c.moveToNext();
 		}
		c.close();

	return entries;
}

まずは、SQLiteDatabaseクラスのqueryメソッドを使い、レコードを取得します。

今回は、queryメソッドの第三引数に一致条件を指定していないので、すべてのレコードが読み出されます。

queryメソッドの実行が成功すると、Cursorクラスのハンドを返します。CursorクラスのgetCountメソッドを呼び出して、レコードの総数を取得して、entriesに格納場所を確保しています。

後は、件数分だけループを回して、レコードの読み出しを行います。

forループの中では、CursorクラスのgetStringメソッドを使ってフィールドを読み取ります。

getStringの第一引数に指定する値は、読み取りたいカラムの位置になります。

これは0から始まります。⁠caption」を取得する場合は1を指定します。フィールドを読み取ったら、moveToNextメソッドで読み出し位置を移動します。

パフォーマンスチューンと利用の注意点

SQLiteは、いかがでしょうか?

組み込み向けと言っても、他のデータベースシステムと大きく使い方が変わるワケではないので、初回から手応えを感じている方も多いのではないでしょうか?そのSQLiteですが、以下に注意して使うと、アプリのパフォーマンスを向上させることができるので、頭に入れておいて下さい。

  • 読み込むフィールド数は、出来る限り少なくする
  • レコードは、まとめて読み込み、まとめて表示する
  • 読み込みレコード数が分かっている場合は、query実行時にlimitを設定する

Android OSに搭載されているSQLiteは、スレッドセーフでコンパイルされていないようで、複数のスレッドから使うと場合によってはクラッシュします。マルチスレッド下で使う場合は、排他処理を行うか自前でハンドルを管理する必要がありますが、筆者の場合は、SQLiteDatabaseハンドルへの参照回数をDBOpenHelperで管理して対応しています。詳細は、サンプルプログラムのDBOpenHelperのgetWritableDatabase, getWritableDatabase、MainStoreのコンストラクタのコードを参照して下さい。

まとめ

今回は、SQLiteに関して、以下のご説明をしました。

  • テーブルの作成
  • アップグレード処理
  • レコードの追加
  • レコードの更新
  • レコードの読み出し

多くのアプリがSQLiteを使ってレコード読み込み・保存をはずなので、サンプルプログラムを修正する試行錯誤して自分の物にしてください。

おすすめ記事

記事・ニュース一覧