Realmでデータを保存する時の注意点 (Managed / Unmanagedの区別, copyToRealm()をいつ使うか)

#はじめに
Realmを使い始めてまだ間もない頃、`copyToRealm()`の役割を理解しないままコードを書いていたせいで、バグを連発してしまいました。これは、そのときの自分に読ませたい記事です。

# 教訓： ManagedObjectとUnmanagedObjectの違いを理解してコードを書こう
Realmのオブジェクトには `ManagedObject` と `UnmanagedObject` の２パターンがあります。
これに関して、[Realmの公式ドキュメント](https://realm.io/docs/java/latest/)(Realm Java 5.9.1)には、次のような説明があります。

> Instances of Realm objects can be either managed or unmanaged.

> Managed objects are persisted in Realm, are always up to date and thread confined. They are generally more lightweight than the unmanaged version as they take up less space on the Java heap.
Unmanaged objects are just like ordinary Java objects, they are not persisted and they will not be updated automatically. They can be moved freely across threads.
It is possible to convert between the two states using Realm.copyToRealm and Realm.copyFromRealm.

上記をGoogle翻訳にかけたあと、微修正したものがこちらです。

>Realmオブジェクトのインスタンスは、**Managed**(=管理対象)または**Unmanaged**(=管理対象外)のどちらでも構いません。
> **ManagedObject**はRealmに永続化され、常に最新の状態に保たれ、スレッドは制限されます。これらはJavaヒープ上の占有スペースが少ないので、一般的に**UnmanagedObject**よりも軽量です。 
**UnmanagedObject**は、通常のJavaオブジェクトとまったく同じです。永続化されていないため、自動的に更新されることはありません。 これらはスレッド間で自由に移動できます。 
Realm.copyToRealmとRealm.copyFromRealmを使用することで、2つの状態を切り替えることができます。


ポイントは、
`UnmanagedObject` の値は自動更新されないため、適宜`Realm.copyToRealm()`を使う必要がある、ということです。

## 実装例: ToDoオブジェクトの保存 (2通り)

例えば、次のようなクラスをRealmで管理したいとします。

```java

public class ToDo extends RealmObject{
    // タイトル
    public String title;
    // 日付
    public String updateDate;
    // 内容
    public String content;
    // チェックボックスがチェックされているか
    public boolean isChecked;    
}
```

このToDoクラスのオブジェクトを**最初に**実装するときは、`ManagedObject` と `UnmanagedObject` のどちらでも構いません。

```java
                //  ManagedObjectとして実装
                ToDo todo = realm.createObject(ToDo.class);

                //  UnmanagedObjectとして実装
                ToDo todo = new ToDo();
```

なぜなら、先ほどの公式ドキュメントの引用文中にもあったように、処理の途中で`UnmanagedObject`を`ManagedObject`に変更したいときは`Realm.copyToRealm()`を使うことで解決でき、`ManagedObject`を`UnmanagedObject`に変更したいときは`Realm.copyFromRealm()`を使うことで解決できるからです。

ただし、大抵のRealm入門記事では`ManagedObject`として実装されています。(自分調べ)


### ToDoオブジェクトの保存メソッド
オブジェクトの持つ値を保存するときは、そのオブジェクトが`ManagedObject` なのか `UnmanagedObject` なのかを区別して処理を書く必要があります。

例として、ToDoオブジェクトの保存メソッド`save()`を用意しました。

まずは、次の枠組みを見てください。

```java:saveメソッドの枠組み
    public void save(final String title, final String updateDate, final String content){
        realm.executeTransaction(new Realm.Transaction(){
            @Override
            public void execute(Realm bgRealm){

                // ここに、値を保存するための処理を書く。

            }
        });
    }
```

この枠組みの中に、オブジェクトの状態に合わせた処理を書く必要があります。

#### ManagedObjectを保存するとき
`ManagedObject`の値は自動で更新されるため、通常のJavaオブジェクトと同じような処理で構いません。

```java:枠組みの中の処理
                ToDo todo = realm.createObject(ToDo.class);
                // 保存したい値を代入
                todo.title = title;
                todo.updateDate = updateDate;
                todo.content = content;
```


#### UnmanagedObjectを保存するとき
`UnmanagedObject`の値は自動で更新されないため、最後に`Realm.copyToRealm()`を使う必要があります。

```java:枠組みの中の処理

                ToDo todo = new ToDo();
                // 保存したい値を代入
                todo.title = title;
                todo.updateDate = updateDate;
                todo.content = content;
                // UnmanagedObjectからManagedObjectへと変更してRealmへコピー
                realm.copyToRealm(todo);
```

# 一言まとめ
Realmを使うときは、オブジェクトが`Managed`なのか`Unmanaged`なのかに注意してコードを書きましょう。