Scala で Morphia を使った MongoDB 操作
MongoDB のための Java 用オブジェクトマッピングツール Morphia を Scala で使ってみました。(Morphia は Ruby の Mongoid や MongoMapper と同等のツール)
使用した環境は以下の通りです。
- Scala 2.9.0
- sbt 0.7.5
- Morphia 1.0 Snapshot
- MongoDB 1.9.0
サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20110515/
sbt プロジェクトファイルの作成
sbt で Morphia を使用するための設定は以下のようになります。
project/build/MorphiaSample.scala
import sbt._ class MorphiaSample(info: ProjectInfo) extends DefaultProject(info) { //Morphia のリポジトリ設定 lazy val morphiaSnapshot = "Morphia Repo at Google Code" at "http://morphia.googlecode.com/svn/mavenrepo" //Morphia を使用するための設定 lazy val morphia = "com.google.code.morphia" % "morphia" % "1.00-SNAPSHOT" override def mainClass = Some("fits.sample.Sample") }
モデルクラスの作成
Morphia では以下のようなアノテーションを使ってモデルクラスを定義します。
- @Entity で永続化対象の指定
- @Embedded で組み込みの指定
- @Reference で参照の指定
- @Id で ID の指定
また、モデルクラスには以下のような注意点があります。
- デフォルトコンストラクタが必要
- デフォルトではクラス名がコレクション名になる(@Entity の value で変更可)
- デフォルトではコレクションへの保存時に className フィールドにクラス名が設定される(@Entity の noClassnameStored=true で保存しないように変更可)
それでは、id:fits:20110306 や id:fits:20110409 と同じモデル構成を Morphia 用に Scala で実装してみます。
以下の設定を行い User・Book・Comment のモデルクラスを作成しました。(Comment は Book に組み込まれるので @Entity は不要)
- @Entity の value を使ってコレクション名を指定
- @Entity の noClassnameStored = true で永続化ドキュメントにクラス名が挿入される事を防止
src/main/scala/User.scala
package fits.sample import com.google.code.morphia.annotations._ import org.bson.types.ObjectId @Entity(value = "users", noClassnameStored = true) class User(var name: String) { //デフォルトコンストラクタ def this() = this("") @Id var id: ObjectId = null }
src/main/scala/Book.scala
package fits.sample import java.util.{List, ArrayList} import com.google.code.morphia.annotations._ import org.bson.types.ObjectId @Entity(value = "books", noClassnameStored = true) class Book(var title: String, var isbn: String) { //デフォルトコンストラクタ def this() = this("", "") @Id var id: ObjectId = null //組み込みの定義(java.util.ArrayList を使用) @Embedded var comments: List[Comment] = new ArrayList[Comment]() }
src/main/scala/Comment.scala
package fits.sample import java.util.Date import com.google.code.morphia.annotations._ class Comment { def this(content: String, user: User) = { this() this.content = content this.user = user } var content: String = "" var createdDate: Date = new Date() //参照の定義 @Reference var user: User = null
実行クラスの作成
今回はスタンドアロン用の実行クラスを作成しました。
new Morphia().createDatastore() で取得した DataStore を使ってドキュメントの操作(追加・更新・削除・検索)を行います。
なお、Scala の場合は save の実行時に型指定する必要があるみたいです。
src/main/scala/Sample.scala
package fits.sample import scala.collection.JavaConversions._ import com.google.code.morphia.Morphia import com.mongodb.Mongo object Sample { def main(args: Array[String]) { //接続設定 val mongo = new Mongo("localhost") val db = new Morphia().createDatastore(mongo, "book_review") //User保存 val u1 = new User("user1") db.save[User](u1) //User保存 val u2 = new User("tester1") db.save[User](u2) //Book保存 val b1 = new Book("ドメイン駆動設計", "9784798121963") b1.comments.add(new Comment("test", u1)) b1.comments.add(new Comment("test2", u2)) //保存(Book 型を明示する必要あり) db.save[Book](b1) //User更新 val tempU1 = db.get(classOf[User], u1.id) tempU1.name = "user1-updated" db.save[User](tempU1) //Commentを追加してBook更新 val tempB1 = db.get(classOf[Book], b1.id) tempB1.comments.add(new Comment("aaaaaaaa", tempU1)) db.save[Book](tempB1) //Book全件取得 db.find(classOf[Book]).asList.foreach {b => printf("book: id = %s, title = %s, isbn = %s\n", b.id, b.title, b.isbn) b.comments.foreach {c => printf(" comment: %s, date = %s, user = %s\n", c.content, c.createdDate, c.user.name) } } } }
動作確認
事前に MongoDB を起動しておきます。
MongoDB 起動
> mongod -dbpath db
sbt を使って実行します。
実行例
> sbt run ・・・ [info] Running fits.sample.Sample ・・・ book: id = 4dcf4c21c55ab7ea6762b8bf, title = ドメイン駆動設計, isbn = 9784798121963 comment: test, date = Sun May 15 12:44:33 JST 2011, user = user1-updated comment: test2, date = Sun May 15 12:44:33 JST 2011, user = tester1 comment: aaaaaaaa, date = Sun May 15 12:44:33 JST 2011, user = user1-updated ・・・
実行後、mongo コマンドを使って保存されたドキュメントの内容を確認してみると以下のようになりました。
mongo コマンドでの確認(books コレクションの内容)
morphia_sample> mongo MongoDB shell version: 1.9.0 > use book_review switched to db book_review > db.books.find() { "_id" : ObjectId("4dcf449bfe76b7ea826f2ec7"), "title" : "ドメイン駆動設計", "isbn" : "9784798121963", "comments" : [ { "content" : "test", "user" : { "$ref" : "users", "$id" : ObjectId("4dcf449bfe76b7ea806f2ec7") }, "createdDate" : ISODate("2011-05-15T03:12:27.337Z") }, { "content" : "test2", "user" : { "$ref" : "users", "$id" : ObjectId("4dcf449bfe76b7ea816f2ec7") }, "createdDate" : ISODate("2011-05-15T03:12:27.337Z") }, { "content" : "aaaaaaaa", "user" : { "$ref" : "users", "$id" : ObjectId("4dcf449bfe76b7ea806f2ec7") }, "createdDate" : ISODate("2011-05-15T03:12:27.383Z") } ] }