えいのうにっき

主に Web 系技術ネタ。背景画像 is powered by grass-graph.shitemil.works

GAE/J の Datastore API では、Map を保存できない・・・けど、

  1. えーの
    a_know うーん、GAEのJDO、データタイプにLinkedHashMapはサポートされてないのかー。CDiTじゃ使いまくってるよ、どーしよーかなー。。
-- this quote was brought to you by quoteurl

↑の件について。Googleの用意してくれているリファレンス(下記引用)にある通り、Map, HashMap, LinkedHashMap・・・などのMapは、サポートしていないようです。

1 つのデータストア プロパティに、複数の値を保持することもできます。JDO では、これをコレクション型の単一フィールド(主な値型またはシリアライズ可能なクラスのコレクション)で表します。以下のコレクション型がサポートされます:

フィールドを List として宣言すると、データストアからは ArrayList 値を保持するオブジェクトが返されます。フィールドを Set として宣言すると、データストアからは HashSet が返されます。フィールドを SortedSet として宣言すると、データストアからは TreeSet が返されます。


同様の問題を記事にされた方もいます。

あるデータを保存したいとする。保存したいデータは entity という役割を持つクラスにまとめる必要があり、最終的に entity のプロパティとして格納する必要がある。これらの仕様は Defining Data Classes - Google App Engine - Google Code に記載されている。

(中略)

Map が使えないとちょっと不便だが、Map を使いたいのならば、素直に、key、value の組み合わせ自体を JDO で書き込んで欲しいということかな?


「Mapが使えないとちょっと不便」、確かに・・・というか、Mapが便利すぎるんですよね。。(苦笑)
そんな私もご多分に漏れず、CDiTにおいては、「曲名と再生記録」「アーティスト名と再生記録」・・・といった情報の取り扱いに、LinkedHashMapを使用しておりました。
CDiTをweb化していくにあたって、gaeサーバにこういったオブジェクトを送信するところまではすんなり実現できたのですが、このオブジェクトをデータストアに格納させることが、上記の問題によりできなかった。Mapを継承したクラスを手作りしてみてもダメ。サポートされてるクラスの要素として放り込んでもダメ。
データの持ち方を工夫して、そもそもLinkedHashMapの使用を止める・・・というのも考えたんですが。でも、現行のCDiTは思いっきりLinkedHashMapをファイルに書き出しちゃってるし、ランキングテーブル編集のロジックも、できればそのまま使いたい・・・、、もどかしいっ。

なんかズルい&盲点がありそうなんですが、回避策。

で、今日お風呂に入りながらふと「こうしたらどーなるんだろ??」というのを思いつき、寝る前にちょっと試してみたところ、エラーも起きず登録することができたので、ご紹介。

下記のような、Mapをフィールドに持つクラスを作り、それをMapの代わりに使うだけです。

public class HandMadeLinkedHashMap implements Serializable{

	private LinkedHashMap<String, Object> innerMap;

	public HandMadeLinkedHashMap(LinkedHashMap<String, Object> param){
		this.innerMap = new LinkedHashMap<String, Object>(param);
	}

	public void put(String key,Object value){
		this.innerMap.put(key, value);
	}

	public Object get(String key){
		return this.innerMap.get(key);
	}
	
   (他に何か欲しいメソッドがあれば)
	
}


んで、こいつをフィールドに持つデータクラスを作ってやるとー。

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class CDiTInfo {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private long id;

    @Persistent
    private String yyMMddHHmmss;

    @Persistent
    private String infoName;

    @Persistent(serialized = "true")
    private ArrayList<Object> sortedListBySongPlayCount;

    @Persistent(serialized = "true")
    private HandMadeLinkedHashMap sortedMapByAlbumPlayCount;

    @Persistent(serialized = "true")
    private HandMadeLinkedHashMap sortedMapByArtistPlayCount;

    public CDiTInfo(long id, String yyMMddHHmmss, String infoName, 
                    ArrayList sortedListBySongPlayCount, 
                    LinkedHashMap sortedMapByAlbumPlayCount, 
                    LinkedHashMap sortedMapByArtistPlayCount) {
        this.id= id;
        this.yyMMddHHmmss = yyMMddHHmmss;
        this.infoName = infoName;
        this.sortedListBySongPlayCount = sortedListBySongPlayCount;
        this.sortedMapByAlbumPlayCount = new HandMadeLinkedHashMap(sortedMapByAlbumPlayCount);
        this.sortedMapByArtistPlayCount = new HandMadeLinkedHashMap(sortedMapByArtistPlayCount);
    }

  (以下、アクセサ)


サポートされてない型でもGAEは受け取ることは(当然のことながら)できますので、受け取ったものを使ってnewしてやれば。シャローコピーだとダメな場合はセットしなおすとか(そういうメソッドを書いてもいいし)。


もしかして周知のテクニックだったりして・・・?
もしくは、何か大きな盲点・落とし穴があったり?


とりあえずはコレで開発を進めてみようかと思います。。。オヤスミナサイ。。