ちょっと話題の記事

[Android Tips] ActiveAndroid を使って ActiveRecord ライクに SQLite を操作する

2014.01.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ActiveRecord スタイルで SQLite を操作

今回は ActiveRecord スタイルの SQLite の ORM (Object Retationship Mapper) である ActiveAndroid のご紹介です。 iOS では MagicalRecord が主流ですが、Android で実現するには ActiveAndroid って感じですね。ちなみに Apache License 2.0 なので商用でも使うことができます。以下のような特長があります。

  • DB モデルの作成
  • レコードの保存・更新・削除
  • カスタムクエリの作成
  • 型のシリアライズ
  • スキーママイグレーション
  • ContentProvider 対応
  • SQLite ファイルのインポート

ということで導入方法から使いかたまでをまとめてみました。

ActiveRecord をビルドする

ActiveRecord 使うには、まずプロジェクトをローカルに持ってきてから自分でビルドしないといけません。ということで、まずはここからソースファイルを直接ダウンロードするか、github から clone してきます。

git clone https://github.com/pardom/ActiveAndroid.git

次に Jar ファイルをビルドするわけですが、Ant、Gradle のどちらかお好きなほうでビルドしましょう。ここでは Gradle でビルドします。

sh gradlew build

Ant の場合は dist に、Gradle の場合は build に Jar ファイルが生成されるので、これをプロジェクトの libs フォルダにコピーして使います。Maven を使っている場合は以下のように pom.xml に dependency を追加します。

<dependency>
	<groupId>com.activeandroid</groupId>
	<artifactId>activeandroid</artifactId>
	<version>(insert latest version)</version>
</dependency>

ActiveRecord を使ってみる

初期設定

それでは使ってみましょう。ActiveAndroid では AndroidManifest.xml に DB 名と DB バージョンを定義する必要があるので記述します。また、android:namecom.activeandroid.app.Application に設定します。カスタムの Application クラスを使っている人は、継承するクラスを com.activeandroid.app.Application に変更する必要があります。

<manifest ...>
	<application android:name="com.activeandroid.app.Application" ...>

		...

		<meta-data android:name="AA_DB_NAME" android:value="sample.db" />
		<meta-data android:name="AA_DB_VERSION" android:value="1" />

	</application>
</manifest>

DB モデルクラスの作成

次に DB モデルクラスを作成します。Model クラスを継承し、アノテーションを追加するだけなので非常に簡単です!テーブル名には @Table、カラム名には @Column を付けます。

@Table(name = "Items")
public class Item extends Model {

	@Column(name = "Name")
	public String name;
	@Column(name = "Category")
	public Category category;

        public Item(){
                super();
        }
        public Item(String name, Category category){
                super();
                this.name = name;
                this.category = category;
        }

}

@Table および @Column に設定できるオプションは次のとおりです。

アノテーション 名前 説明
@Table name テーブル名
@Table id ID
@Column name カラム名
@Column length 長さ (デフォルトは -1)
@Column notNull Not Null 制約 (デフォルトは false)
@Column onNullConflict Null 制約違反のときの動作 (デフォルトは FAIL)
@Column onDelete 削除したときの外部キーの動作
@Column onUpdate 更新したときの外部キーの動作
@Column unique ユニーク制約 (デフォルトは false)
@Column onUniqueConflict ユニーク制約違反のときの動作 (デフォルトは FAIL)

リレーションシップを作りたい場合は以下のように getMany() メソッドを使って実装します。この場合、Category : Item が一対多のリレーションシップを持ちます。

@Table(name = "Categories")
public class Category extends Model {

	@Column(name = "Name")
	public String name;

	public List<Item> items() {
		return getMany(Item.class, "Category");
	}

}

保存・更新・削除・クエリ

作成したモデルクラスを使って DB に保存したりクエリで取得したりするには以下のように実装します。これぞ ActiveRecord スタイル。見やすく分かりやすく素晴らしいですね!

// 保存
Item item = new Item();
item.name = "Sample";
item.save();

// 全件取得
List<Item> items = new Select().from(Item.class).execute();
for (Item i : items) {
    // 更新
    i.name = "Update Sample";
    i.save();
}

// 1件取得
item = Item.load(Item.class, 1);

// 削除
item.delete();

SQL クエリは where() メソッドや orderBy() メソッドを組み合わせて柔軟に作成することができます。

new Select().from(Item.class).where("name = ?", "Sample").orderBy("Name ASC").execute();

また、トランザクションを使うこともできます。

// トランザクション開始
ActiveAndroid.beginTransaction();
try {
    for (int i = 0; i < 10; i++) {
        Item item = new Item();
        item.name = "Sample" + i;
        item.save();
    }
    // トランザクション完了
    ActiveAndroid.setTransactionSuccessful();
} finally {
    // トランザクション終了
    ActiveAndroid.endTransaction();
}

型のシリアライズ

ActiveAndroid では、SQLite で扱えるカラムの基本型以外の型を自分で作ることができます。例えば Date 型は SQLite のままでは直接 DB に入れることはできませんが、型をシリアライズすることで、あたかも Date 型で保存しているかのように Model クラスを実装することができます。Date 型をシリアライズするには、まず以下のようなクラスを作ります。getSerializedType()serialize() は Date 型から Long 型に変換する処理に必要なメソッドで、 getDeserializedType()deserialize() は Long 型から Date 型に変換する処理に必要なメソッドです。つまり、Model クラスでは Date 型として取り扱い、SQLite に保存するときに Long 型に変換して保存します。

final public class UtilDateSerializer extends TypeSerializer {
    @Override
    public Class<?> getDeserializedType() {
        return Date.class;
    }

    @Override
    public Class<?> getSerializedType() {
        return Long.class;
    }

    @Override
    public Long serialize(Object data) {
        if (data == null) {
            return null;
        }

        return ((Date) data).getTime();
    }

    @Override
    public Date deserialize(Object data) {
        if (data == null) {
            return null;
        }

        return new Date((Long) data);
    }
}

作成したシリアライザを扱うには、AndroidManifest.xml に以下を追加します。カンマ区切りで複数のシリアライザを登録することができます。

<meta-data android:name="AA_SERIALIZERS" android:value="パッケージ.UtilDateSerializer" />

SQLite で扱うことのできる型は INTEGER, REAL, TEXT, BLOB の4つです。これに上手く変換することができれば、どんな型でも保存できるようになるというわけです。素晴らしい!

ContentProvider の対応

ContentProvider に対応させることもできます。この機能は実現はされていますが、まだ master ブランチにマージされていないので以下のツリーから clone して Jar ファイルをビルドしないといけません。

https://github.com/dirong/ActiveAndroid/tree/921c703e7181fcecae6f07d5d46b7249c3fdcbc5

ContentProvider に対応するには、まず AndroidManifest.xml に provider を追加します。

<provider
   android:name="com.activeandroid.content.ContentProvider"
   android:authorities="YOUR_PACKAGE_NAME" />

次に Model クラスに _id カラムを追加します。

@Table(name = "Items", id = BaseColumns._ID)
public class Item extends Model {

...

}

これで、以下のように取得できるようになります。CursorLoader に直接突っ込んで Spinner でそのまま使うといったようなことができます。アプリ内だけで使うにはメリットはあまりありませんが、他のアプリから使えるように DB を公開するためには必要になります。

CursorLoader loader = new CursorLoader(this, ContentProvider.createUri(Item.class, null), null, null, null, null);

スキーママイグレーション

ActiveRecord では DB のバージョンを元にマイグレーションしてくれる機能もあります。例えば、一度リリースしたあとに DB にテーブルを追加したりカラムを追加したりといった変更を加えるときに使います。テーブルを追加する場合は Model クラスを追加するだけで良いですが、既存のテーブルに変更を加える場合は、まず AndroidManifest.xml に記載している AA_DB_VERSION を変更します(1だったら2にするなど)。次に既存の Model クラスにカラムを追加します。

@Table(name = "Items", id = BaseColumns._ID)
public class Item extends Model {

    ...
    
    @Column(name = "Color")
    public Integer color;
    
    ...

}

次に assets/migration フォルダに バージョン番号.sql というファイルを作成し、カラムを追加する SQL 文を書きます。

ALTER TABLE Items ADD COLUMN Color INTEGER;

これで終わりです。ActiveRecord は assets/migration に置かれている SQL ファイルをバージョン番号順に実行してくれるので、既存の DB に変更が加わります。

SQLite ファイルのインポート

予め用意してあるデータをインポートするには、assets ディレクトリに AA_DB_NAME と同じファイル名の SQLite ファイルを置きます。あとはここに事前に行っておきたい SQL クエリを書いていくだけです。

CREATE TABLE Items (name TEXT);

INSERT INTO Items (name) VALUES ('aaaaa');
INSERT INTO Items (name) VALUES ('bbbbb');
INSERT INTO Items (name) VALUES ('ccccc');

これだけで、アプリの初回起動時に読み込んでくれます。SQL 文に間違いがあるとインポートされないので注意してください。

まとめ

ActiveAndroid はこれまで面倒だった SQLite の処理全体をとっても楽にしてくれる素晴らしいライブラリです。 発展途上のためバグもあるようなので、更新情報を確認しておくと良いと思います。 あと mogenerator のようなモデルクラスを自動生成してくれるツールも欲しいところですね。

参考