Play Framework(v2.2.3)からMongoDBを使う

Play初心者がMongoDBを使えるようになるまでのメモ。

# 環境
Mac OS X - 10.9.2
play(java) - 2.2.3
MongoDB - 2.6.1
Eclipse - 4.3.2

# ソースコード
https://github.com/brly/play-morphia-sample

# Morphia
https://github.com/mongodb/morphia
"Morphia is a lightweight type-safe library for mapping Java objects to/from MongoDB."
どうやらこれを使えばよさそうなのでこれを使うことにします。

# play-morphia
http://www.playframework.com/modules/morphia
playへmorphiaを組み込むための公式っぽいモジュールがあったっぽいですが
適用可能なplayのバージョンが1.xのみ、ということで使えません。

# playMongoDemo
公式ドキュメントにMongoDB関連のドキュメントがあんまり無い気がしたので適当に検索してたら
https://github.com/czihong/playMongoDemo
playでしかもmorphiaを使っているデモを見つけられたのでcloneしてplayしようとしたら色々エラーが出て動きませんでした。
どうやらplay自体のバージョンが異なる事が原因でした。
"play migration"とかで検索するとなんだかバージョン間の移行に関するページが沢山出てきて、とりあえずそれに従って進めていましたがうまく行かないため挫折。
2.0, 2.1, 2.2でかなり変わっているようですね。playバージョン管理ツールが欲しくなりましたが存在してませんよね?

# play new myapp
というわけでplayMongoDemoを参考にしつつ、自分で手探りで作っていくことにしました。
## Global.java
playMongoDemoにあったのものをまるまるコピーします。
GlobalSettingsオブジェクトでアプリケーションの起動時にMongoDBに接続しているっぽいですね。
GlobalSettings
- http://www.playframework-ja.org/documentation/2.0.8/JavaGlobal


```Global.java
import java.net.UnknownHostException;

import com.google.code.morphia.Morphia;
import com.mongodb.Mongo;

import controllers.MorphiaObject;
import play.GlobalSettings;
import play.Logger;

public class Global extends GlobalSettings {
  @Override
  public void onStart(play.Application arg0) {
    super.beforeStart(arg0);
    Logger.debug("** onStart **"); 
    try {
      MorphiaObject.mongo = new Mongo("127.0.0.1", 27017);
    } catch (UnknownHostException e) {
      e.printStackTrace();
    }
    MorphiaObject.morphia = new Morphia();
    MorphiaObject.datastore = MorphiaObject.morphia.createDatastore(MorphiaObject.mongo, "test");
    MorphiaObject.datastore.ensureIndexes();   
    MorphiaObject.datastore.ensureCaps();  

    Logger.debug("** Morphia datastore: " + MorphiaObject.datastore.getDB());
  }
}

```

Global.javaで使われているMorphiaObjectもplayMongoDemoで以下のように定義されてるのでコピー。

```MorphiaObject.java
package controllers;
import com.google.code.morphia.Datastore;
import com.google.code.morphia.Morphia;
import com.mongodb.Mongo;

public class MorphiaObject {
  static public Mongo mongo;
  static public Morphia morphia;
  static public Datastore datastore;
}
```

## build.sbt

import文で使われてる"com.google.code.morphia"とか"com.mongodb.Mongo"とかを使うためには、build.sbtに色々追記する必要があります。

```
libraryDependencies ++= Seq(
  javaJdbc,
  javaEbean,
  cache,
  "com.google.code.morphia" % "morphia" % "0.99",
  "org.mongodb" % "mongo-java-driver" % "2.7.3",
  "de.flapdoodle.embed" % "de.flapdoodle.embed.mongo" % "1.36" % "test"
)
```
libraryDependenciesに追記します。
デフォルトだとjavaJdbc,javaEbean,cacheのみが書かれていますが、以下の3個を追加します。一番下のやつはテストを書く時に使います。
その後、追加したライブラリを.classpathとかに反映させるためにplayコンソールに入って
`eclipse with-source=true`とかしたりします。うまく行かない時は`play clean`してもう一度やったりして、今のところはうまく行きました。

## モデル
https://github.com/brly/play-morphia-sample/blob/master/app/models/TestModel.java
playMongoDemoに沿って定義します。このソース例ではメンバ数を減らして、

```schema
{
  name: String,
  value: Int
}
```

こんな感じの簡単なやつです。
あとソースを見れば分かりますがRequiredアノテーションとかも使えます。

## コントローラとルーティング
https://github.com/brly/play-morphia-sample/blob/master/conf/routes
https://github.com/brly/play-morphia-sample/blob/master/app/controllers/Application.java
playMongoDemoに沿って変更。play.data.Formあたりの仕様が変わってるので調整する。

## ビュー
https://github.com/brly/play-morphia-sample/blob/master/app/views/index.scala.html
これはplayMongoDemoのままでも大丈夫だったような気がします。

# 実行
![スクリーンショット 2014-05-18 18.39.58.png](https://qiita-image-store.s3.amazonaws.com/0/31270/a970f4bd-0ce0-f373-2346-8342033e81e9.png)
5個程要素を追加後の画面です。

# テスト
テストを書かないと色々言われるご時世なので書かないといけませんが、環境を整えなければなりません。
MySQLの場合だとplayの`fakeApplication`とか`inMemoryDatabase`を使えば良かったのですが、それは使えないので用意する必要があります。

という訳でまたぐぐったところ、以下の記事を見つけます。
http://www.cubeia.com/2012/03/using-mongodb-for-your-game-data/
見慣れたMorphiaという単語が安心感を与えてくれますが、新たな"EmbedMongo"という単語が出てきます。

## EmbedMongo
というわけで"embedded mongo unit test"とかで検索するといかにもなページがあります。
http://quillbytes.com/display/JAVA/Setting+Up+An+Embedded+MongoDB+Database+for+Unit+Tests
でサンプル例があるのですが、サンプルのテストが書きたい感じとちょっと違います。
例ではDBCollectionとかいうオブジェクトを使ってread/writeをしてます。
`TestModel.java`ではそんな感じに書いてなかったので、どうしたら良いのかよくわかりませんでしたがとりあえず以下のようにしたらfakeぽく動作するようになりました。

```TestModelTest.java
@Before
  public void beforeTest() {
    MorphiaObject.mongo = getMongoClient();
    MorphiaObject.datastore = new Morphia().createDatastore(getMongoClient(), "test");
  }
```

`@Before`で毎回MorphiaObjectのmongoとdatastoreを初期化して書きたかった感じにテストを書きます。

```TestModelTest.java
 @Test
  public void 初期値は空リスト() {
    assertThat(TestModel.all()).isEmpty();
  }
  
  @Test
  public void addしたものが順番に取り出せる() {
    TestModel ts0 = new TestModel();
    ts0.name = "ほとココア";
    ts0.value = 15;
    TestModel.create(ts0);
    
    TestModel ts1 = new TestModel();
    ts1.name = "かふうチノ";
    ts1.value = 13;
    TestModel.create(ts1);
    
    TestModel ts2 = new TestModel();
    ts2.name = "てでざリゼ";
    ts2.value = 16;
    TestModel.create(ts2);
    
    TestModel ts3 = new TestModel();
    ts3.name = "うじまつチヤ";
    ts3.value = 15;
    TestModel.create(ts3);
    
    TestModel ts4 = new TestModel();
    ts4.name = "きりまシャロ";
    ts4.value = 15;
    TestModel.create(ts4);
    
    assertThat(TestModel.all()).hasSize(5);
    assertThat(TestModel.all().get(0).name).isEqualTo("ほとココア");
    assertThat(TestModel.all().get(1).name).isEqualTo("かふうチノ");
    assertThat(TestModel.all().get(2).name).isEqualTo("てでざリゼ");
    assertThat(TestModel.all().get(3).name).isEqualTo("うじまつチヤ");
    assertThat(TestModel.all().get(4).name).isEqualTo("きりまシャロ");
  }
```

という訳で、なんとか環境が出来ました。