XamarinというかAndroidでSQLiteを使う時は、SQLiteOpenHelperというクラスを継承して使います。
こいつのコンストラクタは、コンテキスト、データベース名、カーソルファクトリ、データベースのバージョンという4つの引数を渡すのが一般的です。カーソルファクトリはnullでも良さそうなのですが、今回はカーソルファクトリまで実装してみました。
順を追って説明します。
Cursorの実装
今回は以下のようなテーブルを前提にしています。
createtable People ( id integer primary key autoincrement, name varchar(150) notnull );
このテーブルのデータをタイプセーフに取得するためのカーソルを実装します。カーソルは、SQLiteCursorクラスを継承する形で実装します。コンストラクタは親クラスのを呼び出す形で実装して、テーブルの列に対応するプロパティを定義します。
プロパティ内は、GetColumnIndexメソッドで列名から列のインデックスを取得して、インデックスからGet型名のメソッドを使って値を取得しています。
// カーソルクラスclass PeopleCursor : SQLiteCursor { public PeopleCursor(ISQLiteCursorDriver driver, string editTable, SQLiteQuery query) : base(driver, editTable, query) { } // タイプセーフに列のデータを取るためのプロパティを定義しておくpubliclong Id { get { returnthis.GetLong(this.GetColumnIndex("id")); } } publicstring Name { get { returnthis.GetString(this.GetColumnIndex("name")); } } }
CursorFactoryの実装
次に、CursorFactoryの実装です。こいつは、SQLiteDatabase.ICursorFactoryインターフェースを実装します。実装時のポイントとしてはJava.Lang.Objectを継承しておく点です。これがないとうまく動きません。
NewCursorメソッドで、先ほど作成したPeopleCursorを作成して返します。
// カーソルファクトリ。Java.Lang.Objectを継承するのがポイントsealedclass PeopleCursorFactory : Java.Lang.Object, SQLiteDatabase.ICursorFactory { public Android.Database.ICursor NewCursor(SQLiteDatabase db, ISQLiteCursorDriver masterQuery, string editTable, SQLiteQuery query) { // 自前のカーソルを作って返すreturnnew PeopleCursor(masterQuery, editTable, query); } }
SQLiteOpenHelper
最後に、SQLiteOpenHelperを継承したクラスを作成します。こいつは、コンストラクタで、コンテキスト、データベース名、カーソルファクトリ(nullでもOK)、データベースのバージョンを渡します。
そしてOnCreateメソッドでデータベース作成時の処理(create tableなどを実行する)をして、OnUpgradeメソッドでデータベースのバージョンが上がった時の処理(create tableやalter tableなどを実行する)を行います。
さくっと実装すると以下のような感じになります。
class DatabaseHelper : SQLiteOpenHelper { privateconststring DbName = "sample.db"; privateconststring Table = "People"; privateconstint DbVersion = 1; privateconststring CreateTable = @" create table People ( id integer primary key autoincrement, name varchar(150) not null );"; public DatabaseHelper(Context context) : base(context, DbName, new PeopleCursorFactory(), DbVersion) { } publicoverridevoid OnCreate(SQLiteDatabase db) { // テーブルを作って db.ExecSQL(CreateTable); // テストデータを入れる var values = new ContentValues(); values.Put("name", "tanaka"); db.Insert(Table, null, values); } publicoverridevoid OnUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
Insert, Update, Delete, QueryメソッドがSQLiteDatabaseクラスに定義されているので、これを使ってDBに問い合わせてなんやかんやします。
使ってみる
ActivityでDBに問い合わせを行い、最初の行のデータをTextViewに表示するコード例です。
[Activity(Label = "App10", MainLauncher = true, Icon = "@drawable/icon")] publicclass MainActivity : Activity { privatestring message; protectedoverridevoid OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); var db = new DatabaseHelper(this); var cursor = (PeopleCursor) db.ReadableDatabase.Query("People", new[] { "id", "name" }, null, null, null, null, "id"); cursor.MoveToFirst(); this.message = string.Format("{0} {1}", cursor.Id, cursor.Name); this.FindViewById<TextView>(Resource.Id.Main_Label).Text = this.message; } }
画面にMain_LabelというIDのTextViewがあることを前提としています。先ほど作ったDatabaseHelperクラスのインスタンスを作って、ReadableDatabaseのQueryメソッドでデータを取得しています。テーブル名と取得する列名とソート条件を指定しています。nullのところはgroup byとかhavingとかを指定する箇所です。パラメータ名を見れば使い方がわかると思います。
とってきたカーソルはMoveToFirstを呼び出して最初に移動してデータを取得しています。本番では、trueを返すかどうかチェックしたほうがいいと思います。あと、ループで回してListViewに表示するのが本来の使い方だと思いますが今回はサンプルなので一行目だけデータをとってTextViewに設定してます。
まとめ
ICursorFactoryを実装するときにJava.Lang.Objectを継承するというのに気づくまでにすごい時間かかった…。