Realm for Android

こんにちは [@wasabeef_jp](https://twitter.com/wasabeef_jp)です。

**[Realm for Android](http://realm.io/)**の話をしたいと思います。

![realm.png](https://qiita-image-store.s3.amazonaws.com/0/12099/6fdbb1a0-a6fe-c725-ba64-9e7eb605b335.png)

引用：http://wasabeef.jp/realm-for-android/

# Realmとは

"Realm is a mobile database: a replacement for SQLite & Core Data 
Realm can save you thousands of lines of code & weeks of work, 
and lets you craft amazing new user experiences."

SQLite & Core Dataを置き換えるために作られた Mobile向けのDataBase
元々、iOS向けに作られていましたが、C++で書いているので、Androidにも対応しています。

SQLiteのORMとの速度比較
![benchmarks-android.001.png](https://qiita-image-store.s3.amazonaws.com/0/12099/e46a982f-2fa1-d8e9-432d-8cb24d1d45ed.png)


![benchmarks-android.002.png](https://qiita-image-store.s3.amazonaws.com/0/12099/aaec02ca-c1da-6fd6-59dd-05ca2ebc5d18.png)

![benchmarks-android.003.png](https://qiita-image-store.s3.amazonaws.com/0/12099/370db6fc-e594-d647-b500-092f8e73c859.png)

どうやら速いらしい。


## Setup
公式にjarが置いてありますが、Gradleであれば以下のようにします

```groovy
// build.gradle
compile 'io.realm:realm-android:0.71.+'
```

## Models
ActiveAndroidとかSugarORMと似たように、RealmObjectを継承しておきます。

```java
public class User extends RealmObject {
    private String          name;
    private int             age;

    @Ignore
    private int             sessionId;

    // Standard getters & setters generated by your IDE…
    public String getName() { return name; }
    public void   setName(String name) { this.name = name; }
    public int    getAge() { return age; }
    public void   setAge(int age) { this.age = age; }
    public int    getSessionId() { return sessionId; }
    public void   setSessionId(int sessionId) { 
        this.sessionId = sessionId;
    }
}
```

## Writes
Transactionの開始と終了は必須となります。
先ほど、定義したModelをcreateObjectに渡して
インスタンスを作ってもらい、それに対してデータの設定を行うだけとなります。

```java
// Obtain a Realm instance
Realm realm = Realm.getInstance(this, "wasabeef.realm");

realm.beginTransaction();

// Create a new object
User user = realm.createObject(User.class);
user.setName("Wasabeef");
user.setEmail("chip@wasabeef.jp");

realm.commitTransaction();
```

## Queries
メソッドチェインにも対応していて、とてもシンプルに書けます。
ただ、SQLiteではないので、SQL文を使うことは出来ません。

```java
// Build the query looking at all users:
RealmQuery<User> query = realm.where(User.class);

// Add query conditions:
query.equalTo("name", "Wasabeef");
query.or().equalTo("name", "Chip");
// Execute the query:
RealmResults<User> resultAll = query.findAll();

// Or alternatively do the same all at once (the "Fluent interface"):
RealmResults<User> result = 
                       realm.where(User.class)
                            .equalTo("name", "Wasabeef")
                            .or()
                            .equalTo("name", "Chip")
                            .findAll();
```

## Sorting
Asc, Descにも対応してます。

```java
// Query
RealmResults<User> result = realm.where(User.class).findAll();

// Asc
RealmResults<User> sortedAscending  = result.sort("age");

// Desc
RealmResults<User> sortedDescending = 
        result.sort("age", RealmResults.SORT_ORDER_DECENDING);
```

## Deletion
```java
// All changes to data must happen in a transaction
realm.beginTransaction();

// remove single match
result.remove(0);
result.removeLast();

// Delete all matches
result.clear();

realm.commitTransaction()
```

## RealmObject & RealmResults
```java
The only rule to using Realm across threads is to remember that Realm, RealmObject or RealmResults instances cannot be passed across threads. When you want to access the same data from different threads, you should simply obtain a separate Realm instance for each thread (i.e. Realm.getInstance(this) or its cousins) and get your objects through a RealmQuery. The objects will map to the same data on disk, and will be readable & writeable from any thread!

Thread間での受け渡しの想定はしてないので
Realm.getInstance()とRealmQuery()を通して再取得する必要がある
```

## Migration
Libraryで吸収しきれて無いので、結構めんどうくさいです。

```java
public class Migration implements RealmMigration {
    @Override
    public long execute(Realm realm, long version) {

        // Migrate from version 0 to version 1
        if (version == 0) {
            // 自分で、Tableの再構築を行う必要がある
            // 省略
            version++;
        }
    }
}
```

## 抱えてる問題点 (Latest 0.71.0)

* **ModlesにSetter/Getterしか書けない** 

　Debugなどで使いたいので、せめてtoString()くらいは書きたい

```
Error:(12, 19) エラー: Only getters and setters should be defined in model classes
注意:Processing class Person
```

* **EnumはSupportしてない** 

　プロパティにEnumがあると、Buidが通らない

```
Error:(21, 8) エラー: Type io.realm.examples.intro.model.Cat.HogeType of field type is not supported
```

* **RealmObjectをGSONでSerialize出来ない**

```
 java.lang.StackOverflowError
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:376)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:381)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:376)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:381)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:376)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:381)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:376)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:381)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:376)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:381)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:376)
```


* **Migrationがイケてない**

本当にイケてない、いや他のORMとかの考え方が綺麗なだけのかも。


## Realm Browser

Realm DataBaseをGUIで閲覧・編集できるツールも用意されてます。

![browser.png](https://qiita-image-store.s3.amazonaws.com/0/12099/7667ad73-5745-dd3c-f64a-0fd530d3cc58.png)


## 雑感

Realmは、SQLiteを使っていません。
独自にFormatで、DataBaseを作成してます。

特に保存先を指定しなければ、以下のFile DirにRealm Formatで保存されます。
/data/data/パッケージ名/files/

まだVersionも1.xに行っていないので、今後に期待したいと思ってます。