気楽なソフト工房

プログラミングについていろいろな記事を書いています。



mykonos2008

Author:mykonos2008
システムエンジニアとして働いている30代の会社員です。
仕事や趣味でプログラムを書いている方の役に立つ記事を書いていきたいと思っています。
ご意見、ご感想はこちらまで
If you are an english speaker,Please visit my english blog.

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
さて、本日は実際にデータクラスの作成を行ってみますが、その前に
もう1つおさえておきたい点があります。それはEntity Groupとトランザクションについてです。
Entity Groupもエンティティ間の関係と同じく、データクラスの定義の仕方に影響を与えます。

「Entity Group」について
GAEのデータストアではトランザクションを扱うことが出来ます。複数のエンティティに対する
変更を1つのトランザクション内で行い、全て成功したらコミット、1つでも失敗したらロールバックしたり
することが出来るのです。

しかし、これには1つの条件があります。1つのトランザクション内で行われる複数のアクションの対象エンティティが
同一のEntity Groupに属している必要があるということです。

Entity Groupはルートエンティティをトップにその子供のエンティティ、子供の子供(孫)のエンティティという様に
木構造を形をとるエンティティの集合です。

1つのEntity Groupに属するエンティティは大規模な分散システムによって成り立っているGAEのインフラの中で、
任意の単一のノード内で管理されるようになります。

前回の記事で、紹介した所有関係にある複数のエンティティでは、子のエンティティは、自動的に親のエンティティと
同じEntity Groupに属するようになります。

親を持たないエンティティは自動的にそれ自体で1つのEntity Groupを構成するルートエンティティとなります。
ルートエンティティが子孫を持つか持たないかは強制される問題ではありません。

同じく前回の記事で扱った所有関係でない複数のエンティティは、自動的に同じEntity Groupに属するということは
ありません。しかし、処理を施すことによって同一のEntity Groupに属するようにすることも可能です。

このあたりを踏まえて、データクラスの作成に入ります。

所有関係にあるエンティティの作成
まず、所有関係にある「台帳」と「物品種別」を作成してみます。「台帳」は「Ledger」というクラス名で、
「物品種別」は「GoodsKind」というクラス名で作成します。

下記は「Ledger」クラスのコードです。

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Ledger {

   @PrimaryKey
   @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
   private Long ledgerID;

   @Persistent
   private String ledgerName;

   @Persistent
   private Date createdOn;

   @Persistent
   private User createdBy;

   //セッター、ゲッター省略
}

前回の記事でお話しましたように、JDOではデータクラスとなるクラスが特別なインターフェースを実装したり
する必要はありません。その代わりにエンハンサが解釈するためのアノテーションを記述します。

まず、クラスのアノテーションとして、@PersistenceCapable(identityType = IdentityType.APPLICATION)を記述します。

データクラスは必ず1つ、主キーとなるフィールドを持つ必要があります。上記のコードではLong型の「ledgerID」に、
@PrimaryKeyを追加し、主キーとなるようにしています。その下の@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)アノテーションは
このフィールドが永続化の対象でありかつ、永続化される時に自動的に一意の値が割り当てられるようにするためのものです。

その他のフィールドにも@Persistentを付加し、永続化の対象になるようにしています。
逆に永続化しないフィールドについては、@NotPersistentを記述します。

永続化対象フィールドはprivate、またはprotectedである必要があります。そのため、他のクラスからそれらを参照するための
セッターとゲッターを定義します。

「Ledger」クラスは親を持たないルートエンティティです。それに対し、次に紹介する「GoodsKind」は「Ledger」クラスを
親とするエンティティです。この違いによって主キーの定義の仕方が変わります。

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class GoodsKind {

   @PrimaryKey
   @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
   private Key key;

   @Persistent
   private String name;

   @Persistent
   private String additionalDesc;

   //セッター、ゲッター省略
}

「GoodsKind」クラスでは、主キーに「key」という型を使用しています。エンティティはEntityGroup内の先祖のキーと自身のキーに
よって構成されるパスにより一意に識別されます。ファイルシステム内のファイルがディレクトリとファイルの名前によって構成される
パスによって識別されることと似ています。「GoodsKind」クラスで使用しているKeyクラスは、このパスの情報を管理するためのクラスなのです。

「Ledger」クラスはルートエンティティであり、パス情報を持つ必要が無かったので、long型のキーを使用しました。
ルートエンティティのキーとして使用できる型として他にString型があります。

※本来は「Ledger」クラスのキーはLong型で良いはずですが、現状ではクエリを使って、「GoodsKind」クラスのインスタンスを
抽出し、結果を取得する際に「java.lang.ClassCastException: com.google.appengine.api.datastore.Key cannot be cast to java.lang.Long」
が発生します。これは「Ledger」クラスのキーもKey型で定義することで回避できます。


@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Ledger {
// ...

   @PrimaryKey
   @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
   private Key key;
// ...
}

ルートエンティティ以外のキーとして使用できる型としては、他に「Key as Encoded String」というキー情報を文字列と
して表現するタイプの型があります。

さて、この段階ではまだ、「Ledger」クラスと「GoodsKind」クラスの親子関係を定義できていません。
まず、「Ledger」クラスに以下を追加します。

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Ledger {

// ...

   @Persistent(mappedBy = "ledger")
   private List<GoodsKind> goodsKinds;
}

そして、「GoodsKind」クラスに以下を追加します。

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Ledger {
// ...

   @Persistent
   private Ledger ledger;
}

親クラスに子クラスのコレクションのフィールドを定義するだけでも、
親子関係は成立します。しかし、子クラスから親クラスを参照したいケースも多いはずなので、
子クラスにも、親クラスの型のフィールドを追加し、親クラスの @Persistentアノテーションの
mappedByに子クラスのフィールド名を指定しています。

親子関係が便利な理由
親子関係が便利な理由はいくつかあるのですが、まず1つは、
親が削除されるタイミングで、同時に子供も削除されるという点です。

物品管理アプリでは台帳が無くなった物品はゴミになってしまうので、
台帳が無くなるタイミングで物品も自動的に消去されるので、とても便利です。

また、所有関係にあるEntity同士は自動的に同じEntity Group内に属することになります。
これにより、親と子の更新処理を1つのトランザクションで扱うことが可能になります。

台帳に物件を追加するという工程は、実際には、「物品のインスタンスを登録する」という
アクションと「台帳の物品リストに物品を登録する」という2つのアクションに分割されます。
これらは、1つのトランザクション内で行われるべきです。

他には、例えば今回のアプリだと、物品種別を検索する際、必ず物品種別が登録されている台帳が
条件として必要です。そうでないと他の家庭の物品種別まで、見えてしまいます。

そのような場合、以下のようにして台帳で物品種別を絞り込むことが出来ます。

   Query query = pm.newQuery(GoodsKind.class,"ledger == ledgerparam");
   query.declareParameters("String ledgerparam");
   List<GoodsKind> results = (List<GoodsKind>) query.execute(台帳のキーの文字列);

次回は、所有関係でないエンティティ間のリレーションを持つデータクラスを作成してみます。

コメント

コメントの投稿

管理者にだけ表示を許可する

トラックバック

http://csfun.blog49.fc2.com/tb.php/53-3a1e0eb6

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。