2009年10月1日木曜日

pk-nameとencoded-pkはセットです

使用されたタグやその使用回数を保存するクラスをつくり、未使用のタグだったら新規追加、既使用だったら回数増加ということをやっていた。タグの文字列はString型でencoded-pk属性。こんな感じ。
@PrimaryKey
@Persistent
@Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String tagKey;

@Persistent
private Long count;

public Tag(String tagName) {
 this.tagName = tagName;
 count = new Long(1);
}
タグを追加するときの処理。
for (int i = 0; i < tagArray.length; i++) {
 String tag = tagNameList.get(i);
 Key k = KeyFactory.createKey(Tag.class.getSimpleName(), tag);
 try {
  tagArray[i] = pm.getObjectById(Tag.class, k);
  tagArray[i].increment();
 } catch (JDOObjectNotFoundException e) {
  tagArray[i] = new Tag(KeyFactory.keyToString(k));
 }
}
でも、エンコードされた文字列であるtagNameをデコードして見てみると
Tag(文字列1)
Tag(文字列2)
などのように表示される。うむぅ。文字列だけ取得したいな。

そこでGoogleのドキュメント復習。encoded-pkとpk-nameについて読み込み。
データの作成、取得、削除 - Google App Engine - Google Code

pk-nameにエンコードしていない文字列(つまり表示したい文字列)をセットし保存することにした。1プロパティ追加。
@PrimaryKey
@Persistent
@Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String tagKey;

@Persistent
@Extension(vendorName = "datanucleus", key = "gae.pk-name", value = "true")
private String tagName;

@Persistent
private Long count;

public Tag(String tagKey, String tagName) {
 this.tagKey = tagKey;
 this.tagName = tagName;
 count = new Long(1);
}
タグを追加するときはエンコードされたキーとプレーンなStringの両方を引数に与えるようにした。
for (int i = 0; i < tagArray.length; i++) {
 String tag = tagNameList.get(i);
 Key k = KeyFactory.createKey(Tag.class.getSimpleName(), tag);
 try {
  tagArray[i] = pm.getObjectById(Tag.class, k);
  tagArray[i].increment();
 } catch (JDOObjectNotFoundException e) {
  tagArray[i] = new Tag(KeyFactory.keyToString(k), tag);
 }
}
このようにすると、pk-nameを設定したtagNameからプレーンな文字列が取得できるようになった。


と、そこでちょっと考えてみた。

encoded-pkしてStringとして使ってても、結局、文字列を引数にcreateKeyして、keyを文字列へ変換して設定という流れ。ということはKey型の方がシンプルかも?

で、tagKeyの型をStringからKeyへ変更し、tagKeyにつけていた@Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true") を削除。タグを追加するときもkeyToString()しない仕様に変えてみた。
@PrimaryKey
@Persistent
private Key tagKey;

@Persistent
@Extension(vendorName = "datanucleus", key = "gae.pk-name", value = "true")
private String tagName;

@Persistent
private Long count;

public Tag(Key tagKey, String tagName) {
 this.tagKey = tagKey;
 this.tagName = tagName;
 count = new Long(1);
}

:
:
:
//タグを追加するとき
for (int i = 0; i < tagArray.length; i++) {
 String tag = tagNameList.get(i);
 Key k = KeyFactory.createKey(Tag.class.getSimpleName(), tag);
 try {
  tagArray[i] = pm.getObjectById(Tag.class, k);
  tagArray[i].increment();
 } catch (JDOObjectNotFoundException e) {
  tagArray[i] = new Tag(k, tag);
 }
}
そしてテストしてみたら・・・

怒られた。

javax.jdo.JDOFatalUserException: Error in meta-data for hoge.Tag.tagName: A field with the "gae.pk-name" extension can only be used in conjunction with an encoded String primary key..

なるほど。pk-name使いたかったらKey型はNGで、String型のフィールドにencoded-pkのExtension付きじゃないとダメですよ、ということなんですね。了解です。