The HIRO Says

If you smell what The HIRO is cooking!!!

【slim3】永続化対象としての”Model”

GAE ではデータを、BigTableという key-value ベースの DB(key-value store)で管理します。
この”BigTable”へ永続化するクラスとして、slim3 では”Model”というクラスを定義・使用します。


1.Modelの作成方法

前回の記事の、”gen-model”をご確認下さい。


2.Modelの概要

Model は、一言でいうと JavaBeans です。
基本的に、永続化対象のデータ項目をインスタンス変数として定義し、その getter/setter を定義すればOK、という作りになっています。


3.Modelの詳細

Model は、slim3BigTable で使用されることから、単なる JavaBeans には見られない以下のような特徴を持ちます。
※以下は、Model 生成時に自動的に用意されます。


(1)java.io.Serializable を implement している
あわせて、定数 serialVersionUID が定義されます。


(2)クラス定義に @Model アノテーションがつく

  • このアノテーションにより、Meta クラス(後述)が生成されます。
  • org.slim3.datastore.Model クラスを参照。


(3)主キーを持つ

  • com.google.appengine.api.datastore.Key 型の変数 key を持ちます。
    • Model クラスには、主キーが1つ必要。
  • 同変数には、@Attribute(primaryKey=true) アノテーションがつきます。
    • org.slim3.datastore.Attribute クラスを参照。


(4)楽観チェック用の version を持つ

  • Long 型の変数 version を持ちます。
  • 同変数には、@Attribute(version = true) アノテーションがつきます。
    • org.slim3.datastore.Attribute クラスを参照。


(5)schemaVersion を持つ。

  • Integer 型の変数 schemaVersion を持ちます。
  • ※意味がよく分からないです。。。
    • GAE バージョンアップに備え…という情報があるが、裏がまだとれていないです。

※2010/02/07(日)追記
「データ定義」のバージョンを保持する項目(SVNバージョン番号みたいなもの)。
例えばデータ項目を追加した場合に、古い schemaVersion のレコードに一律にデフォルト値をセットする、というような使い方ができる。
※bluerabbitさん、ご指摘ありがとうございました。


4.データ項目の追加方法

永続化対象のデータ項目を追加する方法は、次の通りです。

  1. private なインスタンス変数を定義します。
  2. 上記のインスタンス変数の getter/setter を定義します。


ちなみに、使用できるデータ型は、こちらに定義されています。


なお、変数としては定義したいけれども永続化対象にはしたくない!という時には、変数定義に @Attribute(persistent=false)アノテーションを定義すればOKです。


データ項目の追加例は、当記事の一番下にある定義例をご確認下さい。


5.Metaクラスの自動生成

Model クラスをコンパイルまたは実行すると、<プロジェクトルート>/.apt_generated/<アプリケーションのルートパッケージ>/meta 以下に、Meta.java というクラスが作成されます。
ここに、Model の詳細実装が入っています。


データ項目を追加・変更してコンパイルすると、この Meta クラスが自動的に修正されます。


6.単体テスト

IndexController や Service とは異なり、特別なクラスの継承は不要です。
テスト内容としては、普通の JavaBeans のテストで問題ありません。


ただ1点だけ、テスト用に Key を生成する方法がわかりません。
無理やり Key を生成しようとすると、以下のエラーが出てしまいます。

java.lang.NullPointerException:
No API environment is registered for this thread.

これについては、引き続き調査をしていきます。
※2010/02/07(日)追記
テスト実施用に環境設定を行う必要がある。
暫定的な対応としては、org.slim3.tester.LocalServiceTestCase を extend すると回避可能。
(後日、正式な調査・報告をします。)
※bluerabbitさん、ご指摘ありがとうございました。


7.【参考】Model の定義例

「質問」と「回答」の2つのフィールドを持つ、Q&A の Model を作成する例を以下にあげます。

package xxx.yyy.model.qanda;

import java.io.Serializable;

import org.slim3.datastore.Attribute;
import org.slim3.datastore.Model;

import com.google.appengine.api.datastore.Key;

@Model
public class QAndA implements Serializable {

// fields
    private static final long serialVersionUID = 1L;

    @Attribute(primaryKey = true)
    private Key key;

    @Attribute(version = true)
    private Long version;

    private Integer schemaVersion = 1;

    // 追加したフィールド
    private String question;
    private String answer;



// getter
    public Key getKey() {
        return this.key;
    }

    public Long getVersion() {
        return this.version;
    }

    public Integer getSchemaVersion() {
        return this.schemaVersion;
    }

    // 追加した getter
    public String getQuestion() {
        return this.question;
    }

    // 追加した getter
    public String getAnswer() {
        return this.answer;
    }


// setter
    public void setKey(Key key) {
        this.key = key;
    }

    public void setVersion(Long version) {
        this.version = version;
    }

    public void setSchemaVersion(Integer schemaVersion) {
        this.schemaVersion = schemaVersion;
    }

    // 追加した setter
    public void setQuestion(String question) {
        this.question = question;
    }

    // 追加した setter
    public void setAnswer(String answer) {
        this.answer = answer;
    }
}