読者です 読者をやめる 読者になる 読者になる

えいのうにっき

あたまのなかのデトックスを、不定期的に。主に Web 系技術ネタ。

GAE/J DatastoreへMapを保存 つづき。

GAE/J プログラム

kazunori_279@a_know #appengine のDatastoreでMapですが、シリアライズすれば入りますよね? List Propertyとして使いたいということですか? ( 2009-09-07 12:28:26 )
a_know@kazunori_279 シリアライズアノテーションを付記しても、ビルド、コンパイル時点でエラーが吐かれるのです。帰宅次第、内容をご連絡させて頂きます。 ( 2009-09-07 15:18:44 )
twitterのまとめツールは どんジレさん。これは便利♪)


以前の僕のつぶやきか、もしくはここのエントリに@kazunori_279さんが反応して下さいました。Mapは、単にシリアライズすれば入るのでは?とのこと。
シリアライズアノテーションは私も知っていて、既に試し済みだったのですが、エラーの内容を見ていただこうと思って帰宅してから色々とやってみたところ、少し現象の原因がわかった気がしましたので、この場でまとめてみたいと思います。

Mapの「入れ子」がダメだったみたい。

結論からいうと、こういうことのようです。
前回のエントリではそこまで記述していなかった(一般的な例示をしていた)のですが、実はCDiTでのLinkedHashMapの使用の仕方が↓のようなものだったんですね。

	@Persistent(serialized = "true")
	private LinkedHashMap<String, LinkedHashMap<String, Object>> sortedMapByAlbumPlayCount;

このような入れ子構造にしていると、アノテーションシリアライズを宣言していても、↓のようなエラーがコンパイル・ビルド時に発生してしまいます。(出ているエラーの意味もちょっと分かりかねるのですが。。)

Errors were encountered when loading the specified MetaData files and classes. See the nested exceptions for details
2009/09/07 22:33:45 org.datanucleus.enhancer.DataNucleusEnhancer main
DataNucleus Enhancer completed with an error. Please review the enhancer log for full details. Some classes may have been enhanced but some caused errors
DataNucleus Enhancer completed and no classes were enhanced. Consult the log for full details
致命的: DataNucleus Enhancer completed with an error. Please review the enhancer log for full details. Some classes may have been enhanced but some caused errors
Errors were encountered when loading the specified MetaData files and classes. See the nested exceptions for details
org.datanucleus.exceptions.NucleusUserException: Errors were encountered when loading the specified MetaData files and classes. See the nested exceptions for details
	at org.datanucleus.metadata.MetaDataManager.loadClasses(MetaDataManager.java:384)
	at org.datanucleus.enhancer.DataNucleusEnhancer.getFileMetadataForInput(DataNucleusEnhancer.java:723)
	at org.datanucleus.enhancer.DataNucleusEnhancer.enhance(DataNucleusEnhancer.java:525)
	at org.datanucleus.enhancer.DataNucleusEnhancer.main(DataNucleusEnhancer.java:1235)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.google.appengine.tools.enhancer.Enhancer.execute(Enhancer.java:57)
	at com.google.appengine.tools.enhancer.Enhance.<init>(Enhance.java:60)
	at com.google.appengine.tools.enhancer.Enhance.main(Enhance.java:41)
Caused by: java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class
	at org.datanucleus.jdo.metadata.JDOAnnotationReader.processMemberAnnotations(JDOAnnotationReader.java:1737)
	at org.datanucleus.metadata.annotations.AbstractAnnotationReader.getMetaDataForClass(AbstractAnnotationReader.java:169)
	at org.datanucleus.metadata.annotations.AnnotationManagerImpl.getMetaDataForClass(AnnotationManagerImpl.java:136)
	at org.datanucleus.metadata.MetaDataManager.loadAnnotationsForClass(MetaDataManager.java:2195)
	at org.datanucleus.metadata.MetaDataManager.loadClasses(MetaDataManager.java:343)
	... 10 more
Nested Throwables StackTrace:
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class
	at org.datanucleus.jdo.metadata.JDOAnnotationReader.processMemberAnnotations(JDOAnnotationReader.java:1737)
	at org.datanucleus.metadata.annotations.AbstractAnnotationReader.getMetaDataForClass(AbstractAnnotationReader.java:169)
	at org.datanucleus.metadata.annotations.AnnotationManagerImpl.getMetaDataForClass(AnnotationManagerImpl.java:136)
	at org.datanucleus.metadata.MetaDataManager.loadAnnotationsForClass(MetaDataManager.java:2195)
	at org.datanucleus.metadata.MetaDataManager.loadClasses(MetaDataManager.java:343)
	at org.datanucleus.enhancer.DataNucleusEnhancer.getFileMetadataForInput(DataNucleusEnhancer.java:723)
	at org.datanucleus.enhancer.DataNucleusEnhancer.enhance(DataNucleusEnhancer.java:525)
	at org.datanucleus.enhancer.DataNucleusEnhancer.main(DataNucleusEnhancer.java:1235)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.google.appengine.tools.enhancer.Enhancer.execute(Enhancer.java:57)
	at com.google.appengine.tools.enhancer.Enhance.<init>(Enhance.java:60)
	at com.google.appengine.tools.enhancer.Enhance.main(Enhance.java:41)


ところが、次に示すような書き方だと問題なく登録まで実施できちゃいます。

	@SuppressWarnings("unchecked")
	@Persistent(serialized = "true")
	private LinkedHashMap<String, LinkedHashMap> sortedMapByAlbumPlayCount;
	@Persistent(serialized = "true")
	private LinkedHashMap<String, Object> sortedMapByArtistPlayCount;

パラメータ指定をせずSuppressWarningsで警告を抑止するか、LinkedHashMapが入るところをObjectとしちゃうか。
どちらも気持ち悪いっちゃ気持ち悪いのですが、前者の警告を抑止するよりは、後者の方法の方がいいかな? 兎にも角にも、前回エントリのようなお手製のクラスを用意する必要はなさそうです(あれの方がもっと気持ち悪いw)。

結論

  1. 入れ子構造にさえしていなければ、アノテーション(『@Persistent(serialized = "true")』)を付記することでMapは格納可能!
  2. 入れ子構造にする場合は、パラメータで指定する型を変えるか、SuppressWarningsで警告を抑止する。


・・・「気持ち悪い」「気持ち悪くない」じゃなく、一度ちゃんとJavaジェネリクスを勉強した方がいいかもなぁ。。。(あまり深い問題と捉えてなかった。)