ポストSQLiteのRealmを触ってみる


## Realmって？

簡潔に言うと「Mobile向けNoSQL系データベース」です。
特にこのRealmは、SQLite(Android)とCoreData(iOS)を置き換えることを目的として開発されているとのこと。大きく分けて、Java用とCOCOA用とが用意されてます。
一番の特徴は、その圧倒的なパフォーマンスのようです。確かにこの[公式サイト](http://realm.io/)のデモは驚きました。
他にもいろいろ優れた点があるようなので、興味ある方は公式サイト見るといいと思います。

とにかく触ってみるのが一番だと思ったので、SQLiteとの比較とか無しにコードを書いてみました。

## Realm for Java

Androidアプリで使うために必要な要件です。

- AndroidStudio 0.8.6以上
- 最新のAndroidSDK
- JDK 7以上
- API Level9以上

## インストール方法

AndroidStudioであれば、dependencyに書いてもいいしダウンロードしたJarを入れてもいいようです。
Eclipseでも使えるようですが、だいぶ面倒くさい手順を踏まないといけないので、必要な人は公式サイト見てください。

**Mavenの場合**

1. build.gradleに書く（※ repositoriesにはjcenterを使う）

```gradle:build.gradle
compile 'io.realm:realm-android:0.73.1'
```

**Jarの場合**

1. [ここから](http://static.realm.io/downloads/java/realm-java-0.73.1.zip)リリースパッケージをダウンロードする
2. libsにrealm-VERSION.jarをコピー
3. build.gradleに書く

```gradle:build.gradle
compile files('libs/realm-VERSION.jar')
```

## 使ってみる

### モデルクラスを作る

RealmObjectのサブクラスを作るだけ。
GetterとSetterは自分で書かないといけないらしいけど、「AndroidStudioのGenerator使えば簡単だよね」ってことみたいです。
使える型は、`boolean, short, ìnt, long, float, double, String, Date, byte[]`です。

```java:TestModel.java
public class TestModel extends RealmObject {

    private String name;
    private String favorite;

    public void setName(String name) {
        this.name = name;
    }

    public void setFavorite(String favorite) {
        this.favorite = favorite;
    }

    public String getName() {
        return name;
    }

    public String getFavorite() {
        return favorite;
    }
}
```

### Writeしてみる

追加、変更、削除をするときは必ずTransaction処理の中で行う必要があるとのこと。
特に新しくデータを作成するときは、`createObject()`でインスタンスを生成するのがルールみたいです。

```java:MainActivity.java
    Realm realm = Realm.getInstance(this);

    realm.beginTransaction();

    TestModel model = realm.createObject(TestModel.class);
    model.setName("name 1");
    model.setFavorite("coffee");

    realm.commitTransaction();
```

### Queryを発行してみる

公式サイトに書いてあるけど、ちょっと以下の点に注意しないといけない。
> All fetches (including queries) are lazy in Realm, and the data is never copied.

特に取得した結果はコピーじゃなくてオブジェクトの参照が返ってくるから、変更したり削除したりする場合は、前述したように必ずTransaction内でやらないとダメなようです。

メソッドチェーンでスッキリ書けるのは、気持ちいい。

```java:MainActivity.java
// 普通の書き方
RealmQuery<TestModel> query = realm.where(TestModel.class);
query.equalTo("name", "name 1");
RealmResults<TestModel> result1 = query.findAll();

// メソッドチェーンを使える
RealmResults<TestModel> result2 = realm.where(TestModel.class)
                                       .equalTo("name", "name 1")
                                       .findAll();
```

### Auto-Refresh機能を試す

面白いなーと思ったのが、同じLooper内（通常はUIスレッド）でRealmのインスタンスと更新処理が行われた場合に、自動的にインスタンスが最新版のデータを参照するようになるようです。

原文の一部抜粋です。

> If a Realm instance is contained in a thread that is associated with a Looper (the UI thread is by default) then the Realm instance comes with an auto-refresh feature. This means that the Realm instance will be automatically updated to the latest version on every occurrence of the event loop. 

試しに以下のような実験をしてみました。
`setAutoRefresh(true)`で有効化させてます。

1. 初期化ボタンを押すと新規作成した"coffee"のデータをTextViewにセットする
2. 更新ボタンを押すと1のデータを"green tea"に書き換えてTextViewにセットする

上記を動かしてみると、キャプチャのようになりました。
同じTestModel(RealmObject)のインスタンスを使ってるのに、ちゃんとデータの中身が更新されてるのが分かりますね。
すごい、これは便利ですね。

```java:MainActivity.java
    private Realm mRealm;
    private TestModel mModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRealm = Realm.getInstance(this);
        mRealm.setAutoRefresh(true);

        findViewById(R.id.update_button1).setOnClickListener(this);
        findViewById(R.id.update_button2).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.update_button1:
                mRealm.beginTransaction();
                mModel = mRealm.createObject(TestModel.class);
                mModel.setName("name 1");
                mModel.setFavorite("coffee");
                mRealm.commitTransaction();
                break;
            case R.id.update_button2:
                mRealm.beginTransaction();
                mModel.setFavorite("green tea");
                mRealm.commitTransaction();
                break;
        }
        setTextView();
    }

    private void setTextView() {
        ((TextView) findViewById(R.id.label_view)).setText(mModel.getFavorite());
    }
```

![realm_refresh1.png](https://qiita-image-store.s3.amazonaws.com/0/4706/707779ff-7320-bb12-fb90-f957763ac435.png "realm_refresh1.png") ![realm_refresh2.png](https://qiita-image-store.s3.amazonaws.com/0/4706/2bbac6d3-ff5c-ef3f-87d3-9d1375c7211f.png "realm_refresh2.png") ![realm_refresh3.png](https://qiita-image-store.s3.amazonaws.com/0/4706/c13d5d15-c347-fca1-8454-634ad2ef2e6d.png "realm_refresh3.png")


## 感想

コードの書き方とかは、ActiveAndroidとかと同じようなイメージで書けるので、AndroidでORMのライブラリ使ったことある人は、かなりすんなり使えそうだなと思いました。公式サイトの[ドキュメント](http://realm.io/docs/java/0.73.0/)は細かく書かれているし、いくつかの機能の説明では[サンプルソース](https://github.com/realm/realm-java/tree/master/examples)もありました。これならプライベート・プロジェクトなどで、試しながら使っていけそうな気がしました。

今回は超基本的な部分しか触ってないけど、書いていて気持ちがいいのもあり、引き続きもっといろんな機能を試してみたいです。

## 参考にしたサイト

- [公式サイト](http://realm.io/)
- [Realm for Android (by @wasabeef_jpさん)](http://qiita.com/wasabeef_jp/items/92bb700e37a0a57fc765)