MongoDBとmungbean
sessionに格納するkey-valueのように、保存する要素の変動が多いオブジェクトを簡単に突っ込めるKVSを探していて、MongoDBに行き着きました。が、標準のJava DriverのAPIがイマイチなので、"Third Party Frameworks and Libs"で紹介されているライブラリを使ってみることにしました。これらのライブラリは大きく3つのカテゴリ(POJO Mappers/Code Generation/Misc)が用意されていますが、スキーマレスという特性を活かすのであればPOJO Mappersの方が良いと考え*1、mungbeanというライブラリを使ってみました。
取りあえずmongodとmongoシェルを使って、testデータベースのfooコレクションにあらかじめデータを登録しておきます。
> db.foo.find() { "_id" : ObjectId("4d8f3719822cd27d3519b47f"), "a" : 1 }
mungbeanのjarを見つけることができなかったので、githubからコードをダウンロードし、mavenでパッケージして使ってます。FooとBarというPOJOを用意して試してみました。まずはFooのみ保存するコードを実行します。
実行後、mongoシェルで確認すると、以下のように保存されていました。
> db.foo.find() { "_id" : ObjectId("4d8f3719822cd27d3519b47f"), "a" : 1 } { "_id" : ObjectId("4dc192b4f66fd9d355b5f19a"), "id" : 1, "created" : ISODate("2011-05-04T17:53:56.386Z"), "name" : "hoge", "bars" : null }
Fooのみ保存するようにしたのは、createBarListを呼び出している行のコメントアウトを外すと、以下のエラーとなる為です。FooのIdプロパティ(int)にBarを設定しようとしている、ということなのですが、そんなはずもなく。
java.lang.RuntimeException: java.lang.IllegalArgumentException: Can not set int field test.entity.Foo.id to test.entity.Bar at mungbean.pojo.FieldDefinition.get(FieldDefinition.java:46) at mungbean.pojo.PojoEncoder.encode(PojoEncoder.java:58) at mungbean.protocol.bson.BSONCoder.write(BSONCoder.java:52) at mungbean.protocol.bson.AbstractBSONArray.encode(AbstractBSONArray.java:57) at mungbean.protocol.bson.BSONCoder.write(BSONCoder.java:52) at mungbean.protocol.bson.AbstractBSONMap.encode(AbstractBSONMap.java:54) at mungbean.pojo.PojoEncoder.encode(PojoEncoder.java:60) at mungbean.protocol.bson.BSONCoder.write(BSONCoder.java:69) at mungbean.protocol.message.InsertRequest.<init>(InsertRequest.java:19) at mungbean.AbstractDBCollection$1.doExecuteWithErrorChecking(AbstractDBCollection.java:77) at mungbean.AbstractDBCollection$ErrorCheckingDBConversation.execute(AbstractDBCollection.java:214) at mungbean.SingleNodeDbOperationExecutor.execute(SingleNodeDbOperationExecutor.java:53) at mungbean.SingleNodeDbOperationExecutor.executeWrite(SingleNodeDbOperationExecutor.java:67) at mungbean.AbstractDBCollection.executeWrite(AbstractDBCollection.java:230) at mungbean.AbstractDBCollection.save(AbstractDBCollection.java:73) at test.MongoTest.testSaveFoo(MongoTest.java:23) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99) at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81) at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34) at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75) at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45) at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:66) at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35) at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42) at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34) at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196) Caused by: java.lang.IllegalArgumentException: Can not set int field test.entity.Foo.id to test.entity.Bar at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source) at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source) at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(Unknown Source) at sun.reflect.UnsafeIntegerFieldAccessorImpl.getInt(Unknown Source) at sun.reflect.UnsafeIntegerFieldAccessorImpl.get(Unknown Source) at java.lang.reflect.Field.get(Unknown Source) at mungbean.pojo.FieldDefinition.get(FieldDefinition.java:44) ... 35 more
コレクションを取得する際にFoo.classを指定しているが故なのか、embeddingにするには違うアプローチが必要なのかわかりませんが…。Barを別のコレクションに保存して、FooにはBarのObjectIdだけ持たせる方が良いのかな。
追記: 2011/05/05
FooのインスタンスにBarのインスタンスのリストを設定するとエラーになる件ですが、mungbeanでPOJOを扱うTestCaseを見ても、複数のPOJOを対象としているケースはありませんでした。
コードを調べてみたところ、PojoEncoder