2009/05/23

GAE/JのDatastoreのはなしとか

なんとなくまとめ。使い込んでないので突っ込みお待ちしてます。

キー

まず、保存したいオブジェクト(Javaオブジェクト:POJO)はオブジェクトとかインスタンスとかいいます。Datastore内に保存されているものはエンティティといいます。(リレーショナルDBならタプルとかレコードとか)。

で、エンティティはDatastore内で一意なキーをもつ必要があって、オブジェクトの対象のフィールドに@PrimaryKeyアノテーションをつけて識別します。

Datastoreで使えるキーは4種類あって、単純なLong、String、そしてKeyとそれをStringにエンコードしたEncoded Key Stringって書いてあるヤツ。

LongだとDatastoreに保存したときに自動的に採番されます。設定もできるみたい。Stringはアプリケーションが勝手につけることができます。例えばE-mailで一意に識別したいとか。

残りはKeyクラスとEncoded Key String。関連を使うときにこいつらを使います。Keyはいくつかの情報をもっていて、1つ目はエンティティの種類(RDBならテーブルみたいなもの。Employee.class.getSimpleName()みたいな感じで取得したJavaのクラス名)。

2つ目はIDで、上記のLongかStringをキーにしたときの値。何も設定しない場合はLongと同じく自動採番されるし、Stringで設定する場合は、KeyFactory.createKey(Employee.class, "キーにしたい文字列")って感じでKeyを作れます。

3つ目は親エンティティの情報。Ownedリレーションで説明します。

リレーション

RDBではおなじみの関連。JDOではOwned RelationshipとUnowned Relationshipの2種類があって、Owned Relationshipは所有関係リレーション、Unowned Relationshipは非所有関係リレーションとでも言えばいいのかな。それぞれ1対1、1対多、多対多があるのだけど、GAE/J版ではOnwnedは1対1、1対多だけ、Unownedはまだ対応が弱いとのことで、何でもできるけど自前で頑張らないといけないことが多い。

Owned

Ownedの場合、親エンティティと子エンティティは互いに独立して存在することができない。GAEのページにある例でいうと、ContactInfo(子)はEmployee(親)がないと存在できない。親を消すと子も消える。

で、子エンティティのキーには、親エンティティの情報を持たせなければならないっぽい。それが、子のキーは、親エンティティのキーをエンティティグループの親として使うっていう意味らしい。

だから、子エンティティにしたいオブジェクトのPrimaryKeyフィールドは、Keyクラスか、Encoded Key Stringにしないとダメ。で、子エンティティを作るときには子エンティティのキーに、親エンティティのKey情報をいれるので、先に親がないとダメってこと。

サンプルにあるPerson has one(またはmany) Favorite Foodみたいな関連で、PersonとFoodが独立して存在する必要がある場合は、OwnedではなくUnownedにする必要があるみたいです。例えばFood(餃子)が好きな人は複数人いる可能性があるので、Food(餃子)のKeyに特定の人のKeyを入れるわけにはいかない。

Ownedにする利点は、EmployeeクラスにContactInfo型のcontactInfoフィールドを追加しておけば、Employeeクラスのエンティティを取得して、contactInfoフィールドにアクセスしたときに自動的にその情報がDatastoreから読み込まれるし、ContactInfoをいじった場合に、Employeeを保存すればContactInfoも一緒に更新される。あとトランザクションを使えるとか。

Unowned

上のPersonとFoodみたいな関連の場合、Unownedリレーションにする必要があるのだけど、現在のGAE/J用JDOではPersonクラスにFood型のフィールドをもたせて対応ができない。どうするかというと対象のFoodのキーを保存するKeyかStringなフィールドを持たせることになる。RDBでの外部キーみたいな感じ。この場合、FoodのキーはKeyクラスじゃなくLongとかでもOK。

この場合、Personを保存したときに一緒に保存されるのはFoodのKeyだけ。Personを読み込んだときにfavoriteFoodのエンティティを読み込むのは自動ではできない。いちいちPerson#getFavoriteFood()とかつくって、return pm.getObjectById(favoriteFood)見たいな感じで自前でロードする必要があるようです。メンドイ。

おまけにPersonとFoodをトランザクション内で一緒に操作するには、同じエンティティグループに入れなきゃいけない。エンティティグループはあんまり調べてないのでまた別途。

検索のしかた

Queryの制限に書いてある通り、OwnedでもUnownedでもJoinというか、関連エンティティの属性を使って検索ができない。例えば、Foodにnameという餃子とかカレーとか食べ物の名前を入れられるフィールドがあるとしよう。で、Person.favoriteFood.nameが餃子な人を検索するといったことができない。

そんな検索するには、一回Foodを検索して、Keyを取得する。で、改めて、PersonでfavoriteFoodと取得したKeyが一致するのを検索する。そんな例を書いて今日はお別れです。メンドイ人は、非正規化して食べ物の名前をPersonに持たしちゃえばいいよ!

// 食べ物クラス package example; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.IdentityType; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Food { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private String name; public Food(String name) { this.name = name; this.key = KeyFactory.createKey(Food.class.getSimpleName(), this.name); } public Key getKey() { return key; } public String getName() { return name; } } //人間クラス package example; import javax.jdo.PersistenceManager; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.IdentityType; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Person { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private String name; @Persistent private Key favoriteFood; public Person(String name, Food favoriteFood) { this.name = name; this.key = KeyFactory.createKey(Person.class.getSimpleName(), this.name); this.favoriteFood = favoriteFood.getKey(); } public Key getKey() { return key; } public String getName() { return name; } public Food getFavoriteFood() { PersistenceManager pm = PMF.get().getPersistenceManager(); try { return (Food) pm.getObjectById(Food.class, favoriteFood); } finally { pm.close(); } } } テストケース public void testFavoriteFood() { //どこかで作っておく。 Food gyouza = new Food("餃子"); PersistenceManager pm = PMF.get().getPersistenceManager(); pm.makePersistent(gyouza); pm.close(); Person a = new Person("餃子好きなAさん", gyouza); Person b = new Person("餃子好きなBさん", gyouza); pm = PMF.get().getPersistenceManager(); pm.makePersistent(a); pm.makePersistent(b); pm.close(); //どこかで、餃子好きなひとを探したくなった! //この例だと、Foodのキーを名前にしているので //createKeyしたキーで検索すればよくて //わざわざFoodを検索する必要はないんだけどね。。。 pm = PMF.get().getPersistenceManager(); Key gyouzaKey = KeyFactory.createKey(Food.class.getSimpleName(), "餃子"); Food food = (Food) pm.getObjectById(Food.class, gyouzaKey); Query q = pm.newQuery(Person.class); q.setFilter("favoriteFood == food"); q.declareParameters("com.google.appengine.api.datastore.Key food"); List gyouzaSuky = (List) q.execute(food.getKey()); assertEquals(2, gyouzaSuky.size()); assertEquals("餃子好きなAさん", gyouzaSuky.get(0).getName()); assertEquals("餃子", gyouzaSuky.get(0).getFavoriteFood().getName()); }

2009/05/12

KeyとかIDとか

GAE/JのDatastore APIを読んだけど、なんかKeyとかIDとかAncestorとかよくわからないなぁと思ってたら、PythonAPIのほうに説明が書いてあった。

GAE/JユーザもPythonAPIを読めということか。

ということでそのうち訳そう。

2009/05/10

Relationships

だらだら翻訳 GAE Datastore For Java 最終回(JPAとLow-level APIは今のとこやらない予定。でもLow-level APIをつかわないと面白くなさそう。Javaは型がしょうがないとはいえ、PythonAPIのダイナミックプロパティとかないとDatastoreおいしくなさそう。EntityつかうとMapっぽく使えるのかなぁ。)

それにしても理解不足とはいえ、英語読解力が悲惨なひどい訳なのであまりいじめないでください。寝ぼけながらなんとなく読んで酷い誤読をしてそう。たぶんかなりしてる。 orz

まぁよい英語の勉強になったということで。

公式サイトはさりげなくどんどん更新されてるので、常に公式を追っておくのが吉です。気が向いたら推敲したいと思うけどたぶんしない。

キーの部分がよくわかってない。全体読んでやっとエンティティグループとかリレーションシップでキーの使い方がでてくるのでもう一度見直そう。

それにしてもOwned/Unowned RelationshipとかとかEager Loadingってなんて訳をあてるのがいいのかなぁ。Eager loadingは欲張りローディングとか書いてあったりするけど微妙だなぁ。Lasyが遅延なので、先読みローディングとかだろうか。

ブログで訳すのはめんどくさいし、メンテ性も悪いしなんかいいのないですかね。翻訳エディタのWiki版みたいなの。

このデザイン細長すぎて読みにくいなぁ。デザインも何とかしないと。。。

Relationships

You can model relationships between persistent objects using fields of the object types. A relationship between persistent objects can be described as owned, where one of the objects cannot exist without the other, or unowned, where both objects can exist independently of their relationship with one another. The App Engine implementation of the JDO interface can model owned one-to-one relationships and owned one-to-many relationships, both unidirectional and bidirectional. Unowned relationships are not yet supported with a natural syntax but you can manage these relationships yourself by storing datastore keys in fields directly. App Engine creates related entities in entity groups automatically to support updating related objects together—but it is the app's responsibility to know when to use datastore transactions.

対象のオブジェクト型のフィールドを利用して、永続化オブジェクト間の関連をモデリングすることができます。永続化オブジェクト間の関連は、もう一つのオブジェクトなしでは存在できないOwned、または関連するオブジェクトがお互いに独立して存在することができるUnownedとして記述することができます。JDOインタフェースのApp Engine実装は、Owned1対1関連とOwned1対多関連、それぞれ単方向または双方向関連をモデリングすることができます。Unowned関連は自然な文法ではまだサポートされていませんが、自身でフィールドに直接キーを保存することによって関連を管理することができます。App Engineは、関連するオブジェクトを一緒に更新できるようにするために自動的にエンティティグループに所属した関連するエンティティを作成します。しかし、いつDatastoreのトランザクションを使うかアプリケーションは知る責任があります。

Owned One-to-One Relationships

You create a unidirectional one-to-one owned relationship between two persistent objects by using a field whose type is the class of the related class.

関連するクラスの型のフィールドを使って、2つの永続化オブジェクト間の、単方向1対1 Owned関連を作ることができます。

The following example defines a ContactInfo data class and an Employee data class, with a one-to-one relationship from Employee to ContactInfo.

次の例は、ContactInfoデータクラスとEmployeeデータクラスを定義しています。EmployeeからContactInfoへ1対1関連があります。

ContactInfo.java

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

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class ContactInfo {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private String streetAddress;

    // ...
}

Employee.java

import ContactInfo;
// ... imports ...

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

    @Persistent
    private ContactInfo contactInfo;

    ContactInfo getContactInfo() {
        return contactInfo;
    }
    void setContactInfo(ContactInfo contactInfo) {
        this.contactInfo = contactInfo;
    }

    // ...
}

The persistent objects are represented as two distinct entities in the datastore, with two different kinds. The relationship is represented using an entity group relationship: the child's key uses the parent's key as its entity group parent. When the app accesses the child object using the parent object's field, the JDO implementation performs an entity group parent query to get the child.

この永続化オブジェクトは、Datastoreでは2つの異なる種類の、異なったエンティティとして表されます。この関連はエンティティグループを使って表されます。子どものキーには、エンティティグループの親としてその親のキーを使います。アプリケーションが親オブジェクトのフィールドを使って子オブジェクトにアクセスすると、JDO実装は子オブジェクトを取得するため、エンティティグループの親クエリを実行します。

The child class must have a key field whose type can contain the parent key information: either a Key, or a Key value encoded as a string. See Creating Data: Keys for information on key field types.

子どものクラスは親キーの情報を含むことができる型のキーフィールドを持たなければなりません。それはKeyかKeyの値を文字列としてエンコードしたものです。キーフィールドの型についての情報はCreating Data: Keysを参照してください。

You create a bidirectional one-to-one relationship using fields on both classes, with an annotation on the child class's field to declare that the fields represent a bidirectional relationship. The field of the child class must have a @Persistent annotation with the argument mappedBy = "...", where the value is the name of the field on the parent class. If the field on one object is populated, then the corresponding reference field on the other object is populated automatically.

両方のクラスのフィールドを使うことにより双方向1対1関連を作ることができます。その子クラスのフィールドには双方向関連を表すフィールドであることをアノテーションで宣言します。子クラスのそのフィールドは、親クラスのフィールドの名前を値としたmappedBy = "..." 引数をもった@Persistentアノテーションを付与しなければなりません。あるオブジェクトのこのフィールドが設定されると、もう一方のオブジェクトの対応する参照フィールドが自動的に設定されます。

ContactInfo.java

import Employee;

// ...
    @Persistent(mappedBy = "contactInfo")
    private Employee employee;

Child objects are loaded from the datastore when they are accessed for the first time. If you do not access the child object on a parent object, the entity for the child object is never loaded. (The datastore interface does not support the "eager" loading of child objects. The datastore does not support join queries, so an implementation of eager loading wouldn't save the app a call to the datastore.)

子オブジェクトが最初にアクセスされたときに、Datastoreからロードされます。親オブジェクトの子オブジェクトに開く背しない場合、そのエンティティの子オブジェクトはロードされません。(Datastoreインタフェースは子オブジェクトの「eager」ローディングをサポートしていません。DatastoreはJoinクエリをサポートしないので、eagerローディングの実装はDatastoreの呼び出しを防ぎません。)

Owned One-to-Many Relationships

To create a one-to-many relationship between objects of one class and multiple objects of another, you use a Collection of the related class:

あるクラスのオブジェクトと、他の複数のオブジェクト間の1対多関連を作るには、関連先クラスのコレクションを使います。

Employee.java

import java.util.List;

// ...
    @Persistent
    private List<ContactInfo> contactInfoSets;

A one-to-many bidirectional relationship is similar to a one-to-one, with a field on the parent class using the annotation @Persistent(mappedBy = "..."), where the value is the name of the field on the child class:

1対多の双方向関連は1対1関連と似ています。親クラスのフィールドに@Persistent(mappedBy = "..."アノテーションを付与します。値は子クラスのフィールドの名前です。

Employee.java

import java.util.List;

// ...
    @Persistent(mappedBy = "employee")
    private List<ContactInfo> contactInfoSets;

ContactInfo.java


import Employee;

// ...
    @Persistent
    private Employee employee;

The collection types listed in Defining Data Classes: Collections are supported for one-to-many relationships. However, arrays are not supported for one-to-many relationships.

Defining Data Classes: Collectionsに一覧されているコレクションの型が1対多関連でサポートされています。

App Engine does not support join queries: you cannot query a parent entity using an attribute of a child entity. (You can query a property of an embedded class, because embedded classes store properties on the parent entity. See Defining Data Classes: Embedded Classes.)

App EngineはJoinクエリをサポートしていません。子エンティティの属性を使って親エンティティを探すクエリを実行することはできません。(埋め込みクラスのプロパティはクエリに使うことができます。なぜなら、埋め込みクラスは親エンティティのプロパティとして保存されるためです。Defining Data Classes: Embedded Classesを参照してください。)

How Ordered Collections Maintain Their Order

Ordered collections, such as List<...>, preserve the order of objects when the parent object is saved. JDO requires that databases preserve this order by storing the position of each object as a property of the object. App Engine stores this as a property of the corresponding entity, using a property name equal to the name of the parent's field followed by _INTEGER_IDX. Position properties are inefficient. If an element is added, removed or moved in the collection, all entities subsequent to the modified place in the collection must be updated. This can be slow, and error prone if not performed in a transaction.

List<...>のような、順序付きコレクションは永続化オブジェクトが保存されたときにオブジェクトの順番を保持します。JDOはデータベースがオブジェクトのプロパティとして、各オブジェクトの場所を保存することによって、この順番を保存することを要求します。App Engineは対応するエンティティのプロパティとしてこれを保存します。それは親のフィールドの名前のあとに_INTEGER_IDXという名前のプロパティ名となります。順序プロパティは効率がよくありません。要素が追加されたとき、コレクションは削除または移動され、続く全てのエンティティはコレクション内の場所を移動するために更新されなければなりません。これはとても遅く、トランザクション内でない場合エラーになる可能性があります。

If you do not need to preserve an arbitrary order in a collection, but need to use an ordered collection type, you can specify an ordering based on properties of the elements using an annotation, an extension to JDO provided by DataNucleus:

コレクションの順番を保持する必要がないが、順序づきのコレクション型を使う必要がある場合は、DataNucleusによってJDOに提供されている拡張アノテーションを使って、要素のプロパティによる順序付けを指定することができます。

import java.util.List;
import javax.jdo.annotations.Extension;
import javax.jdo.annotations.Order;
import javax.jdo.annotations.Persistent;

// ...
    @Persistent
    @Order(extensions = @Extension(vendorName="datanucleus", key="list-ordering", value="state asc, city asc"))
    private List<ContactInfo> contactInfoSets = new List<ContactInfo>();

The @Order annotation (using the list-ordering extension) specifies the desired order of the elements of the collection as a JDOQL ordering clause. The ordering uses values of properties of the elements. As with queries, all elements of a collection must have values for the properties used in the ordering clause.

@Orderアノテーション(list-ordering拡張を使っています)で、JDOQLの順序句としてコレクションの要素の順番を希望するとおり指定します。この順序は要素のプロパティの値を利用します。クエリと同様、コレクションの全ての要素は、順序句で使うプロパティの値を持っていなければなりません。

Accessing a collection performs a query. If the ordering clause of a field uses more than one sort order, the query requires a datastore index. See Queries and Indexes for more information on indexes.

クエリによってコレクションにアクセスします。フィールドの順序句が1つ以上のソート順を使っている場合、クエリはDatastoreのインデックスを必要とします。インデックスの詳細な情報はQueries and Indexesを参照してください。

For efficiency, always use an explicit ordering clause for one-to-many relationships of ordered collection types, if possible.

効率のため、可能であれば順序付きコレクション型の1対多関連のために、常に明示的に順序句を使ってください。

Unowned Relationships

In addition to owned relationships, the JDO API also provides a facility for managing unowned relationships. The App Engine implementation of JDO does not yet implement this facility, but don't worry, you can still manage these relationships using Key values in place of instances (or Collections of instances) of your model objects. You can think of storing Key objects as modeling an arbitrary "foreign key" between two objects. The datastore does not guarantee referential integrity with these Key references, but the use of Key makes it very easy to model (and then fetch) any relationship between two objects. However, if you go this route there are a few additional things to keep in mind. First, it is the app's responsibility to make sure the Keys are of the appropriate kind - JDO and the compiler will not do any type checking for you. Second, all objects must belong to the same entity group in order to perform an atomic update of objects on both sides of the relationship.

Owned関連に加え、JDO APIはUnowned関連の管理をする機能も提供します。JDOのApp Engine実装ではこの機能は未だ実装されていません。しかし、心配しないでください。モデルオブジェクトのインスタンス(もしくはインスタンスのコレクション)のKey値を使って、この関連を管理することができます。2つのオブジェクト間の「外部キー」として、Keyオブジェクトを保存すると考えることができます。DatastoreはこれらのKeyの参照の完全性を保障しませんが、Keyを使うと2つのオブジェクト間のどんな関連も簡単にモデル(そして取得)できるようにします。しかし、この方法を使うとき、いくつか意識しておくことがあります。まず、Keyが適切な種類であることを確かめるのはアプリケーションの責任です。JDOとコンパイラは型チェックを行いません。次に、関連する双方のオブジェクトでアトミックに更新するため、全てのオブジェクトは同じエンティティグループに所属する必要があります。

Tip: In some cases you may find it necessary to model an owned relationship as if it is unowned. This is because all objects involved in an owned relationship are automatically placed in the same entity group, and an entity group can only support 1 to 10 writes per second. So for example if a parent object is receiving .75 writes per second and a child object is receiving .75 writes per second, it may make sense to model this relationship as unowned so that both parent and child reside in their own, independent entity groups.

Tip:いくつかの場合、Owned関連をUnowned関連のようにモデリングする必要があることに気付くかもしもしれません。Owned関連の全てのオブジェクトは自動的に同じエンティティグループに置かれます。そして、エンティティグループは1秒間に1から10回の書き込みしかサポートしてません。よって親オブジェクト親オブジェクトが1秒間に.75回の書き込みをすると、子オブジェクトも1秒間に.75回の書き込みになります。Unownedの関連として、親と子はそれぞれ独立で、独立なエンティティグループに存在するようにみなすかもしれません、

Unowned One-to-One Relationships

Suppose we want to model person and food, where a person can only have one favorite food but a favorite food does not belong to the person because it can be the favorite food of any number of people:

PersonとFootをモデル化したいとします。Personは1つの好きな食べ物を持つことができ、好きな食べ物はPersonに所属しません。なぜなら、その好きな食べ物は何人にでも持つことができるからです。

Person.java

// ... imports ...

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

    @Persistent
    private Key favoriteFood;

    // ...
}

Food.java

import Person;
// ... imports ...

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

    // ...
}

In this example, rather than giving Person a member of type Food to represent the person's favorite food we instead give Person a member of type Key, where the Key is the unique identifier of a Food object. Note that unless an instance of Person and the instance of Food referred to by Person.favoriteFood are in the same entity group, it is not possible to update the person and that person's favorite food in a single transaction.

この例では、PersonのメンバにFood型を与えず、Personの好きな食べ物をKey型のメンバで表現しています。このKeyはFoodオブジェクトのユニークな識別氏です。Personのインスタンスと、Person.favoriteFoodで参照されるFoodのインスタンスは同じエンティティグループに所属していなければ、PersonとPersonの好きな食べ物を単一のトランザクションで更新できないことに注意してください。

Unowned One-to-Many Relationships

Now suppose we want to let a person have multiple favorite foods. Again, a favorite food does not belong to the person because it can be the favorite food of any number of people:

Personが複数の好きな食べ物を持つようにしたいとします。今回も好きな食べ物は何人もの人がもつことができるため、好きな食べ物はPersonに所属していません。

Person.java

// ... imports ...

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

    @Persistent
    private Set<Key> favoriteFoods;

    // ...
}

In this example, rather than giving Person a member of type Set<Food> to represent the person's favorite foods we instead give Person a member of type Set<Key>, where the set contains the unique identifiers of Food objects. Note that unless an instance of Person and an instance of Food contained in Person.favoriteFoods are in the same entity group, it is not possible to update the person and that favorite food in a single transaction.

この例では、PersonにSet型のメンバではなく、好きな食べ物を表すのにSet型のメンバを使っています。これはFoodオブジェクトのユニークな識別しのセットを格納します。Personのインスタンスと、Person.favoriteFoodで参照されるFoodのインスタンスは同じエンティティグループに所属していなければ、PersonとPersonの好きな食べ物を単一のトランザクションで更新できないことに注意してください。

Many-to-Many Relationships

We can model a many-to-many relationship by maintaining collections of keys on both sides of the relationship. Let's adjust our example to let Food keep track of the people that consider it a favorite:

関連の両方でキーのコレクションを管理することによって多対多関連をモデル化することができます。例のFoodに対して、好んでる人を追跡できるようにしてみましょう。

Person.java

import java.util.Set;
import com.google.appengine.api.datastore.Key;

// ...
    @Persistent
    private Set<Key> favoriteFoods;

Food.java

import java.util.Set;
import com.google.appengine.api.datastore.Key;

// ...
    @Persistent
    private Set<Key> foodFans;

In this example the Person maintains a Set of Key values that uniquely identify the Food objects that are favorites, and the Food maintains a Set of Key values that uniquely identify the Person objects that consider it a favorite.

この例ではPersonは好きなFoodオブジェクトのユニークな識別子のKeyのセットを管理し、Foodはそれを好きだとしているPersonのユニークな識別子のKeyのセットを管理します。

When modeling a many-to-many using Key values, be aware that it is the app's responsibility to maintain both sides of the relationship:

Album.java

Keyの値を用いて、多対多関連をモデリングしたとき、関連の双方を管理する責任がアプリケーションにあります。


// ...
public void addFavoriteFood(Food food) {
    favoriteFoods.add(food.getKey());
    food.getFoodFans().add(getKey());
}

public void removeFavoriteFood(Food food) {
    favoriteFoods.remove(food.getKey());
    food.getFoodFans().remove(getKey());
}

Note that unless an instance of Person and an instance of Food contained in Person.favoriteFoods are in the same entity group, it is not possible to update the person and that favorite food in a single transaction. If it is not feasible to colocate the objects in the same entity group, the application must account for the possibility that a person's favorite foods will get updated without the corresponding update to the food's set of fans or, conversely, that a food's set of fans will get updated without the corresponding update to the fan's set of favorite foods.

Personのインスタンスと、Person.favoriteFoodで参照されるFoodのインスタンスは同じエンティティグループに所属していなければ、PersonとPersonの好きな食べ物を単一のトランザクションで更新できないことに注意してください。もしそのオブジェクトを同じエンティティグループに配置できない場合、アプリケーションは、Personの好きな食べ物を更新するときに対応する食べ物のファンのセットを更新されない可能性があることに責任をとる必要があります。反対にFoodのファンのセットが更新されときに対応するファンの好きな食べ物のセットが更新されない場合もあります。

Relationships, Entity Groups, and Transactions

When an object with owned relationships is saved to the datastore, all other objects that can be reached via relationships and need to be saved (they are new or have been modified since they were last loaded) are saved automatically. This has important implications for transactions and entity groups.

DatastoreにOwned関連をもつオブジェクトが保存されるとき、関連を通じてアクセスでき、保存する必要のある(新規またはロードされてから変更された)全てのほかのオブジェクトは自動的に保存されます。これはトランザクションとエンティティグループに関する重要な影響です。

Consider the following example using a unidirectional relationship between the Employee and ContactInfo classes above:

上記のEmployeeとContactInfoクラス間の単方向関連の例を考えて見ましょう。


    Employee e = new Employee();
    ContactInfo ci = new ContactInfo();
    e.setContactInfo(ci);

    pm.makePersistent(e);

When the new Employee object is saved using the pm.makePersistent() method, the new related ContactInfo object is saved automatically. Since both objects are new, App Engine creates two new entities in the same entity group, using the Employee entity as the parent of the ContactInfo entity. Similarly, if the Employee object has already been saved and the related ContactInfo object is new, App Engine creates the ContactInfo entity using the existing Employee entity as the parent.

新しいEmployeeオブジェクトがpm.makePersistent()メソッドで保存されたとき、新しい関連するContactInfoオブジェクトも自動的に保存されます。両方のオブジェクトが新しい場合、App Engineは同じエンティティグループに、ContactInfoエンティティの親としてEmployeeエンティティを使って、2つの新しいエンティティを作成します。同様にEmployeeオブジェクトが既に保存されていて、関連するContactInfoオブジェクトが新しい場合、App Engineは既存のEmployeeオブジェクトを親として、ContactInfoエンティティを作成します。

Notice, however, that the call to pm.makePersistent() in this example does not use a transaction. Without an explicit transaction, both entities are created using separate atomic actions. In this case, it is possible for the creation of the Employee entity to succeed, but the creation of the ContactInfo entity to fail. To ensure that either both entities are created successfully or neither entity is created, you must use a transaction:

しかし、この例ではトランザクションを使用せず、pm.makePersistent()を呼んでいることに注意してください。明示的なトランザクションを使用せず、双方のエンティティが別々のアトミックな操作で作成されています。この場合、Employeeエンティティの作成が成功し、ContactInfoエンティティの作成に失敗する場合があります。両方のエンティティの作成に成功したか、作成されないかを確かにするにはトランザクションを使う必要があります。

    Employee e = new Employee();
    ContactInfo ci = new ContactInfo();
    e.setContactInfo(ci);

    try {
        Transaction tx = pm.currentTransaction();
        tx.begin();
        pm.makePersistent(e);
        tx.commit();
    } finally {
        if (tx.isActive()) {
            tx.rollback();
        }
    }

If both objects were saved before the relationship was established, App Engine cannot "move" the existing ContactInfo entity into the Employee entity's entity group, because entity groups can only be assigned when the entities are created. App Engine can establish the relationship with a reference, but the related entities will not be in the same group. In this case, the two entities cannot be updated or deleted in the same transaction. An attempt to update or delete entities of different groups in the same transaction will throw a JDOFatalUserException.

両方のオブジェクトが関連が成立する前に保存された場合、App Engineは既存のContactInfoエンティティをEmployeeエンティティのエンティティグループに「移動」することはできません。なぜなら、エンティティグループはエンティティが作られたときにのみアサインされるからです。App Engineは参照で関連を張ることができますが、関連するエンティティは同じグループではありません。この場合、2つのエンティティは単一のトランザクションで更新や削除することができません。異なるグループのエンティティを、単一のトランザクション内で更新したり削除したり使用とするとJDOFatalUserExceptionがスローされます。

Saving a parent object whose child objects have been modified will save the changes to the child objects. It's a good idea to allow parent objects to maintain persistence for all related child objects in this way, and to use transactions when saving changes.

子オブジェクトが変更された親オブジェクトを保存すると、子オブジェクトの変更も保存されます。この方法で全ての関連する子オブジェクトを、親オブジェクトで管理するのは良いアイデアです。そして、変更を保存するときにはトランザクションを使用してください。

Dependent Children and Cascading Deletes

The App Engine implementation of JDO makes all owned relationships "dependent." If a parent object is deleted, all child objects are also deleted. Breaking an owned relationship by assigning a new value to the dependent field on the parent also deletes the old child.

JDOのApp Engine実装は全てのOwned関連を「依存」にします。親オブジェクトが削除されると、全ての子オブジェクトもまた削除されます。依存するフィールドに新しい値を代入して、Owned関連を壊すと、古い子は削除されます。

As with creating and updating objects, if you need every delete in a cascading delete to occur in a single atomic action, you must perform the delete in a transaction.

作成や更新も同様に、単一のアトミックな操作でカスケード削除をしたい場合、トランザクションで実行してください。

Note: The JDO implementation does the work to delete dependent child objects, not the datastore. If you delete a parent entity using the low-level API or the Admin Console, the related child objects will not be deleted.

注:JDO実装が、関連する子オブジェクトを削除します、これはDatastoreの処理ではありません。もし低レベルAPIやAdmin Consoleから親エンティティを削除した場合、関連する子オブジェクトは削除されません。

Transactions

つづき。やっとエンティティグループがでてくる。でもキーの理解が足りないのでよくわからない。

Transactions

The App Engine datastore supports transactions. A transaction is an operation or set of operations that either succeeds completely, or fails completely. An application can perform multiple operations and calculations in a single transaction.

App Engine datastoreはトランザクションをサポートしています。トランザクションは完全に成功するか、完全に失敗する一つの操作、または操作のセットです。アプリケーションは複数の操作と計算を単一のトランザクションで実行することができます。

Using Transactions

A transaction is a datastore operation or a set of datastore operations that either succeed completely, or fail completely. If the transaction succeeds, then all of its intended effects are applied to the datastore. If the transaction fails, then none of the effects are applied.

トランザクションは、完全に成功するか完全に失敗する、Datastoreの操作または操作のセットです。そのトランザクションが成功した場合、全ての意図する結果がDatastoreに適用されます。もしトランザクションが失敗した場合結果は一切適用されません。

Every datastore write operation is atomic. An attempt to create, update or delete an entity either happens, or it doesn't. An operation may fail due to a high rate of contention, with too many users trying to modify an entity at the same time. Or an operation may fail due to the application reaching a quota limit. Or there may be an internal error with the datastore. In all cases, the operation's effects are not applied, and the datastore API raises an exception.

全てのDatastoreの書込み操作はアトミックです。エンティティを作成、更新、削除使用とすると、それは実行されるか、またはされません。とても大量のユーザが同時に一つのエンティティを変更しようとする操作は高い確率で競合により失敗します。また、アプリケーションがクオータの制限に達した場合、Datastoreでインターナルエラーが発生した場合も失敗します。全てのケースで、その操作の結果は適用されません。そして、Datastore APIは例外を投げます。

Here is an example of incrementing a field named counter in an object named ClubMembers (class not shown) using the JDO transaction API:

次の例は、JDOのトランザクションAPIを使用して、ClubMembers(クラスは記載してません)という名前のオブジェクトのcounterというフィールドをインクリメントします。

 
import javax.jdo.Transaction;
 
import ClubMembers;   // not shown
 
// ...
        // PersistenceManager pm = ...;
 
        Transaction tx = pm.currentTransaction();
 
        try {
            tx.begin();
    
            ClubMembers members = pm.getObjectById(ClubMembers.class, "k12345");
            members.incrementCounterBy(1);
            pm.makePersistent(members);
    
            tx.commit();
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }

Entity Groups

Every entity belongs to an entity group, a set of one or more entities that can be manipulated in a single transaction. Entity group relationships tell App Engine to store several entities in the same part of the distributed network. A transaction sets up datastore operations for an entity group, and all of the operations are applied as a group, or not at all if the transaction fails.

全てのエンティティはエンティティグループという、単一のトランザクションで操作することのできる、1つ以上のエンティティのセットに所属します。エンティティグループの関連はApp Engineに、いくつかのエンティティが分散ネットワークの同じ部分に格納されるように命じます。トランザクションはエンティティグループ用にDatastore操作をセットアップし、全ての操作をグループとして適用します。トランザクションに失敗した場合は何もしません。

When the application creates an entity, it can assign another entity as the parent of the new entity. Assigning a parent to a new entity puts the new entity in the same entity group as the parent entity.

アプリケーションがエンティティを作成するとき、新しいエンティティの親として別のエンティティをアサインすることができます。新しいエンティティを親にアサインすると、親のエンティティとして、その新しいエンティティを同じエンティティグループにおきます。

An entity without a parent is a root entity. An entity that is a parent for another entity can also have a parent. A chain of parent entities from an entity up to the root is the path for the entity, and members of the path are the entity's ancestors. The parent of an entity is defined when the entity is created, and cannot be changed later.

親を持たないエンティティはルートエンティティです。他のエンティティの親であるエンティティもまた親を持つことができます。あるエンティティからルートまでの親エンティティのチェインは、エンティティのパスです。そして、パスのメンバーはそのエンティティの祖先です。あるエンティティの親は、エンティティが作成されたときに定義され、その後に変更することはできません。

Every entity with a given root entity as an ancestor is in the same entity group. All entities in a group are stored in the same datastore node. A single transaction can modify multiple entities in a single group, or add new entities to the group by making the new entity's parent an existing entity in the group.

ルートエンティティが祖先として与えられた各エンティティは同じエンティティグループとなります。そのグループの全てのエンティティは同じDatastoreのノードに保存されます。単一のトランザクションは、単一のグループの複数のエンティティを変更することができます。また、そのグループに属するエンティティを新しいエンティティの親とすることで、新しいエンティティをそのエンティティグループに追加することができます。

Creating Entities With Entity Groups

The App Engine implementation of the JDO interface represents owned one-to-one or one-to-many relationships using entity groups. This allows changes to an object and changes to its child objects to occur in the same transaction. See Relationships.

JDOインターフェースのApp Engine実装は、エンティティグループを利用してOwned1対1または1対多関連を表すことができます。これにより、対象のオブジェクトの変更と、その個オブジェクトへの変更を同一のトランザクションで行うことができます。Relationshipsを参照してください。

For other situations, you can create an entity with an explicit entity group parent by setting the object's primary key field to the complete key, including the parent's key. The object's primary key field must be either a Key instance or an encoded key string (not a simple Long or String key name).

その他のシチュエーションのため、オブジェクトのプライマリキーフィールドに、親のキーを含む完全なキーを設定することにより、エンティティグループの親を明示してエンティティを作成することができます。そのオブジェクトのプライマリキーフィールドはKeyインスタンスかエンコードされたキー文字列でなければなりません(単純なLongまたはStringキー名は不可)。

When using app-assigned string IDs, you can create an object with an entity group parent by setting its key field to the complete key value, which includes the key of the parent. To create a key value using an entity group parent, use the KeyFactory.Builder class, as follows:

アプリケーションでアサインされた文字列IDを使ったとき、親のキーを含む完全なキーの値をキーのフィールドに設定することによって、エンティティグループの親を指定したオブジェクトを作成することができます。エンティティグループの親を使ってキーの値を作るには、KeyFactoru.Builderクラスを使います。

 
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
 
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class AccountInfo {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;
 
    public void setKey(Key key) {
        this.key = key;
    }
}
 
// ...
        KeyFactory.Builder keyBuilder = new KeyFactory.Builder(Customer.class.getSimpleName(), "custid985135");
        keyBuilder.addChild(AccountInfo.class.getSimpleName(), "acctidX142516");
        Key key = keyBuilder.getKey();
 
        AccountInfo acct = new AccountInfo();
        acct.setKey(key);
        pm.makePersistent(acct);

You can access the entity group parent key separately from the object's key using a field, as follows:

フィールドを使って、オブジェクトのキーとは別にエンティティグループの親のキーにアクセスすることができます。

 
// ...
    @Persistent
    @Extension(vendorName="datanucleus", key="gae.parent-pk", value="true")
    private Key customerKey;

To create an object with a system-generated numeric ID and an entity group parent, you must use an entity group parent key field (such as customerKey, above). Assign the key of the parent to the parent key field, then leave the object's key field set to null. When the object is saved, the datastore populates the key field with the complete key, including the entity group parent.

システムが生成した数値IDとエンティティグループの親でオブジェクトを作るには、エンティティグループの親のキーフィールド(上記ではcustomerKey)を使う必要があります。

If a class has an entity group parent field, you can use an equality filter in a query on the parent field. (Inequality filters on parent keys are not supported.)

エンティティグループの親のフィールドを持つクラスの場合、その親のフィールドに等号フィルタをクエリに使用することができます。(不等号フィルタを親のキーに使用することはサポートされていません。)

What Can Be Done In a Transaction

The datastore imposes several restrictions on what can be done inside a single transaction.

Datastoreは単一のトランザクション内でできることにいくつかの制限を課します。

All datastore operations in a transaction must operate on entities in the same entity group. This includes retrieving entities by key, updating entities, and deleting entities. Notice that each root entity belongs to a separate entity group, so a single transaction cannot create or operate on more than one root entity.

トランザクション内の全てのDatastore操作は、同じエンティティグループのエンティティへの操作でなければなりません。これには、エンティティをキーにより抽出する、エンティティを更新する、エンティティを削除することが含まれます。ルートエンティティは、別のエンティティグループに所属しています。よって単一のトランザクションは1つ以上のルートエンティティを作成したり操作したりすることができません。

An app cannot perform a query during a transaction. However, an app can retrieve datastore entities using keys during a transaction, and be guaranteed that the fetched entity is consistent with the rest of the transaction. You can prepare keys prior to the transaction, or you can build keys inside the transaction with key names or IDs.

アプリケーションはトランザクションの中でクエリを実行することができません。しかし、アプリケーションはトランザクション中にキーを使うことでDatastoreのエンティティを抽出することができます。そして、そのようにして取得したエンティティはその後のトランザクションで一貫性が保たれることが保障されます。トランザクションの前にキーを準備したり、トランザクションの中で、キー名かIDを使ってキーを組み立てることもできます。

An application cannot create or update an entity more than once in a single transaction.

アプリケーションは単一のトランザクションで1回以上エンティティを作成したり更新することができません。

JDO performs all actions between the call to tx.begin() and the call to tx.commit() in a single transaction. If any action fails due to the requested entity group being in use by another process, JDO throws a JDODataStoreException or a JDOException, cause by a java.util.ConcurrentModificationException.

JDOはx.begine()を呼び出してからtx.commit()を呼び出す間で行う全ての操作を単一のトランザクション内で実行します。要求したエンティティグループが、他のプロセスで使われていると、いかなる操作も失敗し、 java.util.ConcurrentModificationExceptionが原因で、JDOはJDODataStoreExceptionかJDOExceptionをスローします。

In a system with optimistic concurrency, it is typical for an application to try the transaction again several times before giving up. JDO only performs the transaction once; the application must repeat the transaction, if desired. For example:

楽観的並行性のシステムでは、アプリケーションはランザクションをあきらめる前に何回かト試行するのが一般的です。JDOはトランザクションを一回しか実行しません。アプリケーションはもし望むならそのトランザクションを繰り返す必要があります。

 
        for (int i = 0; i < NUM_RETRIES; i++) {
            pm.currentTransaction().begin();
 
            ClubMembers members = pm.getObjectById(ClubMembers.class, "k12345");
            members.incrementCounterBy(1);
 
            try {
                pm.currentTransaction().commit();
                break;
 
            } catch (JDOCanRetryException ex) {
                if (i == (NUM_RETRIES - 1)) { 
                    throw ex;
                }
            }
        }

An attempt to update more than one entity group in the same transaction will throw a JDOFatalUserException. Notice that each object that doesn't have an entity group parent resides in its own entity group, so you cannot create multiple parent-less entities in a single transaction.

同じトランザクションで、1つ以上のエンティティグループを更新しようとすると、JDOFatalUserExceptionがスローされます。エンティティグループの親をもたない各オブジェクトは、エンティティグループを所有しています。よって、親のないエンティティを単一トランザクションで複数作成することはできません。

An attempt to update the same entity multiple times in a single transaction (such as with repeated makePersistent() calls) will throw a JDOFatalUserException. Instead, just modify the persistent objects within the transaction, and allow the call to commit() to apply the changes.

単一のトランザクションで、同じエンティティを複数回アップデート(例えばmakePersistent()を繰り返し呼ぶ)しようとすると、JDOFatalUserExceptionがスローされます。その代わり、トランザクション内で対象の永続化オブジェクトを変更し、変更を適用するためcommit()を呼んでください。

Uses For Transactions

This example demonstrates one use of transactions: updating an entity with a new property value relative to its current value.

次にエンティティから現在の値を取り出し、新しいプロパティで更新するトランザクションの例を示します。

 
        Key k = KeyFactory.createKey("Employee", "k12345");
        Employee e = pm.getObjectById(Employee.class, k);
        e.counter += 1;
        pm.makePersistent(e);

This requires a transaction because the value may be updated by another user after this code fetches the object, but before it saves the modified object. Without a transaction, the user's request will use the value of counter prior to the other user's update, and the save will overwrite the new value. With a transaction, the application is told about the other user's update. If the entity is updated during the transaction, then the transaction fails with an exception. The application can repeat the transaction to use the new data.

この例はトランザクションを必要とします。なぜなら、オブジェクトを取得したあとオブジェクトの変更を保存する前に、他のユーザによって対象の値が更新される可能性があるためです。トランザクションがない場合、ユーザの要求は他のユーザが更新する前にcounterの値を使い、そして保存したときに新しい値を上書きしてしまいます。トランザクションを使用した場合、アプリケーションは他のユーザの更新を伝えます。トランザクションの間に対象のエンティティが更新された場合、トランザクションは失敗し例外が発生します。アプリケーションは新しいデータを使うため、トランザクションを繰り返すことができます。

Another common use for transactions is to update an entity with a named key, or create it if it doesn't yet exist:

他のトランザクションの一般的な使い方は、名前付けられたキーでエンティティを更新したり、存在しないときに作成する場合です。

 
        // PersistenceManager pm = ...;
 
        Transaction tx = pm.currentTransaction();
 
        String id = "jj_industrial";
        String companyName = "J.J. Industrial";
       
        try {
            tx.begin();
 
            Key k = KeyFactory.createKey("SalesAccount", id);
            SalesAccount account;
            try {
                account = pm.getObjectById(Employee.class, k);
            } catch (JDOObjectNotFoundException e) {
                account = new SalesAccount();
                account.setId(id);
            }
 
            account.setCompanyName(companyName);
            pm.makePersistent(account);
 
            tx.commit();
 
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }

As before, a transaction is necessary to handle the case where another user is attempting to create or update an entity with the same string ID. Without a transaction, if the entity does not exist and two users attempt to create it, the second will overwrite the first without knowing that it happened. With a transaction, the second attempt will fail atomically, and can be retried by the application to fetch the new entity and update it.

前述の通り、トランザクションは他のユーザが同じ文字列IDを持つエンティティを作成したり更新したりしようとするのをハンドリングする必要があります。トランザクションを使用しない場合、対象のエンティティが存在せず、2人のユーザがそれを作成しようとすると、2人目のユーザが最初の値を知らずに上書きしてしまいます。トランザクションを使用した場合、2人目のユーザの作成は自動的に失敗し、アプリケーションによりリトライを行い、新しいエンティティを取得してそれを更新することができます。

Tip: A transaction should happen as quickly as possible to reduce the likelihood that the entities used by the transaction will change, requiring the transaction be retried. As much as possible, prepare data outside of the transaction, then execute the transaction to perform datastore operations that depend on a consistent state. The application should prepare keys for objects used inside the transaction, then fetch the entities inside the transaction.

Tip:トランザクションはできるだけ早く、そのトランザクションで使われているエンティティがかわるような状態を減らし、トランザクションがリトライされるように要求するべきです。可能性はありますが、トランザクションの外でデータを準備し、一貫性のある状態が必要なDatastoreの操作を実行するトランザクションを実行してくさい。アプリケーションはトランザクション内で使用するオブジェクトのキーを用意し、トランザクションの中でエンティティを取得するべきです。

Disabling Transactions and Porting Existing JDO Apps

The JDO configuration we recommend using sets a property named datanucleus.appengine.autoCreateDatastoreTxns to true. This is an App Engine-specific property that tells the JDO implementation to associate datastore transactions with the JDO transactions that are managed in application code. If you are building a new app from scratch, this is probably what you want. However, if you have an existing, JDO-based application that you want to get running on App Engine, you may want to use an alternate persistence configuration which sets the value of this property to false:

我々が推奨するJDOの設定はdatanucleus.appengine.autoCreateDatastoreTxnsプロパティをtrueに設定することです。これはApp Engine特有のプロパティで、JDO実装にアプリケーションコードで管理されたJDOトランザクションとDatastoreのトランザクションを連携させるように命じます。スクラッチから新しいアプリケーションを構築している場合、これはおそらくあなたが望んできることです。しかし、既にあるJDOベースのアプリケーションをAPP Engineで動作させたい場合、他の永続化設定を使いたいと思うかもしれません。その場合は、このプロパティをfalseに設定してください。

 
<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">
 
    <persistence-manager-factory name="transactions-optional">
        <property name="javax.jdo.PersistenceManagerFactoryClass"
            value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
        <property name="javax.jdo.option.NontransactionalRead" value="true"/>
        <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
        <property name="javax.jdo.option.RetainValues" value="true"/>
        <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="false"/>
    </persistence-manager-factory>
</jdoconfig>

In order to understand why this may be useful, remember that you can only operate on objects that belong to the same entity group within a transaction. Applications built using traditional databases typically assume the availability of global transactions, which allow you to update any set of records inside a transaction. Since the App Engine datastore does not support global transactions, code that assumes the availability of global transactions will yield exceptions. Instead of going through your (potentially large) codebase and removing all your transaction management code, you can simply disable datastore transactions. This of course does nothing to address assumptions your code makes about atomicity of multi-record modifications, but it allows you to get your app working so that you can then focus on refactoring your transactional code incrementally and as needed, rather than all at once.

これがなぜ役に立つのかを理解するために、トランザクション内では同一のエンティティグループに所属するエンティティのみ操作できることを思い出してください。従来のデータベースを使っているアプリケーションは一般的にグローバルトランザクションが使えると仮定するでしょう。それはトランザクション内でどんなレコードでも更新できます。しかしApp Engine datastoreはグローバルトランザクションをサポートしていません。グローバルトランザクションが使えることを仮定しているコードは例外を発生させます。あなたの(とても大きな)コードベースを読み返し、全てのトランザクション管理コードを削除する代わりに、データストアのトランザクションを無効化してください。これはもちろんあなたのコードが複数のレコードの変更をアトミックにすることを仮定して処理するものではありませんが、アプリケーションを動作させることができるようになります。よって、全て一度に行うのではなく、必要であればトランザクションのコードに集中してリファクタリングすることができるようになります。

Queries and Indexes

つづき。一昨日ダウンロードした内容と今の原本のHTMLが微妙にちがってたのでずれてるかも。翻訳エディタ使うかなぁ。

Queries and Indexes

Every datastore query uses an index, a table that contains the results for the query in the desired order. An App Engine application defines its indexes in a configuration file named datastore-indexes.xml. The development web server can automatically generate suggestions for this file as it encounters queries that do not yet have indexes configured.

各Datastoreクエリは、希望の順でクエリの結果を含むテーブルであるインデックスを使います。App Engineアプリケーションはdatastore-indexes.xmlという名前の設定ファイルにインデックスを定義します。開発Webサーバは設定されていないインデックスのクエリに遭遇すると、インデックスを提案し、そのファイルに自動的に書き込みます。

The index-based query mechanism supports most common kinds of queries, but it does not support some queries you may be used to from other database technologies. Restrictions on queries, and their explanations, are described below.

インデックスベースのクエリメカニズムは、ほとんどの一般的な種類のクエリをサポートしています。しかし、他のデータベーステクノロジで使ってきたようないくつかのクエリはサポートしていません。クエリの制限については後述します。

Introducing Queries

A query retrieves entities from the datastore that meet a set of conditions. The query specifies an entity kind, zero or more conditions based on entity property values (sometimes called "filters"), and zero or more sort order descriptions. When the query is executed, it fetches all entities of the given kind that meet all of the given conditions, sorted in the order described.

クエリはコンディションのセットに合致するエンティティをDatastoreから抽出します。このクエリはエンティティの種類、0以上エンティティのプロパティの値に基づくコンディション(「フィルタ」と呼ばれます)と、ゼロ以上のソート順を指定します。クエリが実行されると、与えられた種類と、コンディションに合致するすべてのエンティティを取得し、指定された順にソートします。

A query can also return just the keys of the result entities instead of the entities themselves.

また、クエリはエンティティ自身のかわりに、結果のエンティティの「キー」を返すこともできます。

JDO can perform queries for entities that meet certain criteria. You can also use a JDO Extent to represent the collection of every entity of a kind (every stored object of a class).

JDOは指定されたクライテリアに合致するエンティティのクエリを実行します。また、ある種類の全てのエンティティ(あるクラスの全ての保存されているオブジェクト)を表現するJDO Extentを使用することができます。

Queries with JDOQL

JDO includes a query language for retrieving objects that meet a set of criteria. This language, called JDOQL, refers to JDO data classes and fields directly, and includes type checking for query parameters and results. JDOQL is similar to SQL, but is more appropriate for object-oriented databases like the App Engine datastore. (The App Engine datastore does not support SQL queries with the JDO interface.)

JDOは指定されたクライテリアに合致するエンティティのクエリを実行します。また、ある種類の全てのエンティティ(あるクラスの全ての保存されているオブジェクト)を表現するJDO Extentを使用することができます。

The query API supports several calling styles. You can specify a complete query in a string, using the JDOQL string syntax. You can also specify some or all parts of the query by calling methods on the query object.

このクエリAPIはいくつかの呼び出しスタイルをサポートしています。JDOQL文字列の文法をつかった完全なクエリを指定することができます。また、クエリのいくつかまたは全ての部分を、クエリオブジェクトのメソッドを呼び出すことによって指定することもできます。

Here is a simple example of a query using the method style of calling, with one filter and one sort order, using parameter substitution for the value used in the filter. The Query object's execute() method is called with the values to substitute in the query, in the order they are declared.

これはメソッドスタイルの呼び出しを使ったクエリの例です。1つのフィルタと、1つのソート順を指定し、フィルタのパラメータの代入を行っています。Queryオブジェクトのexecute()メソッドが、前に宣言した代入値とともに呼ばれます。

import java.util.List;
import javax.jdo.Query;

// ...

    Query query = pm.newQuery(Employee.class);
    query.setFilter("lastName == lastNameParam");
    query.setOrdering("hireDate desc");
    query.declareParameters("String lastNameParam");

    try {
        List<Employee> results = (List<Employee>) query.execute("Smith");
        if (results.iterator().hasNext()) {
            for (Employee e : results) {
                // ...
            }
        } else {
            // ... no results ...
        }
    } finally {
        query.closeAll();
    }

Here is the same query using the string syntax:

これは同じクエリを文字列文法を使って記載しています。

    Query query = pm.newQuery("select from Employee " +
                              "where lastName == lastNameParam " +
                              "order by hireDate desc " +
                              "parameters String lastNameParam")

    List<Employee> results = (List<Employee>) query.execute("Smith");

You can mix these styles of defining the query. For example:

これらのスタイルを混ぜてクエリを定義することもできます。

    Query query = pm.newQuery(Employee.class,
                              "lastName == lastNameParam order by hireDate desc");
    query.declareParameters("String lastNameParam");

    List<Employee> results = (List<Employee>) query.execute("Smith");

You can reuse a single Query instance with different values substituted for the parameters by calling the execute() method multiple times. Each call performs the query and returns the results as a collection.

一つのQueryインスタンスをことなるパラメータをexecute()メソッドに渡して何度も呼ぶことで再利用することができます。各呼び出しはクエリを実行して、コレクションとして結果を返します。

The JDOQL string syntax supports value literals within the string for string values and numeric values. Surround strings in either single-quotes (') or double-quotes ("). All other value types must use parameter substitution. Here is an example using a string literal value:

JDOQL文字列文法は文字列と数値のリテラル値をサポートしています。シングルクォートかダブルクォートで文字列を囲んでください。全てのほかの値の型はパラメータ代入を使う必要があります。次は文字列リテラルを使った例です。

    Query query = pm.newQuery(Employee.class,
                              "lastName == 'Smith' order by hireDate desc");

Query Filters

A filter specifies a field name, an operator, and a value. The value must be provided by the app; it cannot refer to another property, or be calculated in terms of other properties. The operator can be any of the following: < <= == >= >

フィルタは、フィールド名、演算子、そして値を指定します。その値はアプリケーションによって提供されなければならず、他のプロパティの値を参照や、他のプロパティに関して計算されることができません。この演算子はつぎのいずれかが使えます:< <= == >= >

Note: The Java datastore interface does not support the != and IN filter operators that are implemented in the Python datastore interface. (In the Python interface, these operators are implemented in the client-side libraries as multiple datastore queries; they are not features of the datastore itself.)

注:Java Datastoreインターフェースでは、Pythonデータストアインターフェースで実装している!=とINフィルタ演算子をサポートしていません。(Pythonインターフェースでは、これらの演算子はクライアントサイドライブラリとして複数のデータストアクエリで実装しています。これらはDatastore自身の機能ではありません)

The subject of a filter can be any object field, including the primary key and the entity group parent (see Transactions).

フィルタの対象には、オブジェクトのフィールド、プライマリキー、エンティティグループの親を含むことができます。(Transactionsを参照)

An entity must match all filters to be a result. In the JDOQL string syntax, multiple filters are specified separated by && (logical "and"). Other logical combinations of filters (logical "or", "not") are not supported.

結果のエンティティは、全てのフィルタにマッチしなければなりません。JDOQLの文字列文法では、複数のフィルタは&& (論理"and")で分割して指定することができます。他の論理演算(論理 "or", "not")はサポートされていません。

Due to the way the App Engine datastore executes queries, a single query cannot use inequality filters (< <= >= >) on more than one property. Multiple inequality filters on the same property (such as querying for a range of values) are permitted. See Restrictions on Queries.

App Engine datastoreがクエリを実行するとき、単一のクエリは一つ以上のプロパティに不等号フィルタ(< <= >= >)を使うことができません。複数の不等号フィルタを同じプロパティ(値の範囲のクエリのような)は許可されています。Restrictions on Queriesを参照してください。

    query.setFilter("lastName == 'Smith' && hireDate > hireDateMinimum");
    query.declareParameters("Date hireDateMinimum");

Query Sort Orders

A sort order specifies a property and a direction, either ascending or descending. The results are returned sorted by the given orders, in the order they were specified. If no sort orders are specified for the query, the results are ordered by their entity keys.

ソート順は、プロパティと方向、昇順か降順を指定します。結果は与えられた順で指定した順でソートされて返されます。もしソート順を指定しなかった場合、エンティティのキー順となります。

Due to the way the App Engine datastore executes queries, if a query specifies inequality filters on a property and sort orders on other properties, the property used with the inequality filters must be ordered before the other properties. See Restrictions on Queries.

App Engine datastoreでクエリを実行するとき、不等号フィルタとソート順を他のプロパティに指定すると、不等号フィルタで使われたプロパティは、他のプロパティの前にソートされなければなりません。Restrictions on Queriesを参照してください。

    query.setOrdering("hireDate desc, firstName asc");

Query Ranges

A query can specify a range of results to be returned to the application. The range specifies which result in the complete result set should be the first one returned, and which should be the last, using numeric indexes, starting with 0 for the first result. For example, a range of 5, 10 returns the 6th, 7th, 8th, 9th and 10th results.

クエリは、アプリケーションに返す結果の範囲を指定することができます。範囲は完全な結果セットからどの範囲の結果かの最初と最後を、最初の結果を0とした数字のインデックスで指定することができます。例えば、5から10の範囲は、6~10番目の結果を返します。

The starting offset has implications for performance: the datastore must retrieve and then discard all results prior to the starting offset. For example, a query with a range of 5, 10 fetches 10 results from the datastore, then discards the first 5 and returns the remaining 5 to the application.

パフォーマンスのため、開始オフセットは明示的に指定します。Datastoreは開始オフセットの前の全ての結果を抽出し排除しなければなりません。例えば、5, 10の範囲を持つクエリは10の結果をDatastoreから抽出し、最初の5個を破棄し、残りの5個をアプリケーションに返します。

    query.setRange(5, 10);

Extents

A JDO Extent represents every object in the datastore of a particular class..

JDO Extentは、Datastore中のある特定のクラスのすべてのオブジェクトをあらわします。

You start an Extent using the PersistenceManager's getExtent() method, passing it the data class. The Extent class implements the Iterable interface for accessing results. When you are done accessing results, you call the closeAll() method.

PersistenceManagerの getExtent()メソッドにデータクラスを渡して使うことによりExtentを開始できます。ExtentクラスはIterableインタフェースを実装しており、結果にアクセスすることができます。結果にアクセスが終わったら、 closeAll()メソッドを呼んでください。

The following example iterates over every Employee object in the datastore:

次にDatastore中の全てのEmployeeオブジェクトを繰り返す例をしめします。

import java.util.Iterator;
import javax.jdo.Extent;

// ...

    Extent extent = pm.getExtent(Employee.class, false);
    for (Employee e : extent) {
        // ...
    }
    extent.closeAll();

An extent retrieves results in batches, and can exceed the 1,000-result limit that applies to queries.

Extentはバッチで結果を抽出し、クエリに適用される1000件までという結果の制限を超えることができます。

Introducing Indexes

The App Engine datastore maintains an index for every query an application intends to make. As the application makes changes to datastore entities, the datastore updates the indexes with the correct results. When the application executes a query, the datastore fetches the results directly from the corresponding index.

App Engine datastoreはクエリごとにアプリケーションの意図に合わせてインデックスをメンテナンスします。アプリケーションがDatastoreのエンティティを変更すると、Datastoreは収集した結果でインデックスを更新します。アプリケーションがクエリを実行したとき、Datastoreは対応するインデックスから直接結果を収集します。

An application has an index for each combination of kind, filter property and operator, and sort order used in a query. Consider the example query, stated in JDOQL:

種類と、フィルタのプロパティと演算子、そしてソート順を使用したクエリを結合したインデックスをアプリケーションは持っています。次のJDOQLクエリの例を考えて見ましょう

select from Person where lastName == "Smith"
                      && height < 72
                order by height desc

The index for this query is a table of keys for entities of the kind Person, with columns for the values of the height and lastName properties. The index is sorted by height in descending order.

このクエリのインデックスはheightとlastNameというプロパティの値のカラムを持つ、Personという種類のエンティティのキーのテーブルです。このインデックスは降順のheightのソートを持っています。

Two queries of the same form but with different filter values use the same index. For example, the following query uses the same index as the query above:

同じ形で、異なるフィルタの値の2つのクエリは同じインデックスを使用します。例えば、次のくえりは上記のクエリと同じインデックスを使用します。

select from Person where lastName == "Jones"
                      && height < 64
                order by height desc

The datastore executes a query using the following steps:

Datastoreは次の手順でクエリを実行します。

  1. The datastore identifies the index that corresponds with the query's kind, filter properties, filter operators, and sort orders.
  2. The datastore starts scanning the index at the first entity that meets all of the filter conditions using the query's filter values.
  3. The datastore continues to scan the index, returning each entity, until it finds the next entity that does not meet the filter conditions, until it reaches the end of the index, or until it has collected the maximum number of results requested by the query.
  1. Datastoreはクエリの種類、フィルタのプロパティ、フィルタの演算子、ソート順と対応するインデックスを識別します。
  2. クエリのフィルタ値を使って全てのフィルタのコンディションに合致する最初のエントリをインデックスから検索します。
  3. フィルタの条件に合致しない次のエンティティが見つかる、インデックスの末尾に達する、クエリの要求する最大の結果数に達するのいずれかが発生するまで、インデックスをスキャンし続けエンティティを返します。

An index table contains columns for every property used in a filter or sort order. The rows are sorted by the following aspects, in order:

インデックステーブルはフィルタもしくはソート順で使用する全てのプロパティのカラムを保持しています。行は次の側面と順でソートされています。

  • ancestors
  • property values used in equality filters
  • property values used in inequality filters
  • property values used in sort orders
  • 祖先
  • 等号フィルタで使用するプロパティの値
  • 不等号フィルタで使用するプロパティの値
  • ソート順で使用するプロパティの値

This puts all results for every possible query that uses this index in consecutive rows in the table.

このテーブルの連続する行の、インデックスで使用するクエリでとりうる全ての結果を保持します。

This mechanism supports a wide range of queries and is suitable for most applications. However, it does not support some kinds of queries you may be used to from other database technologies.

このメカニズムは幅広いクエリをサポートし、ほとんどのアプリケーションに適合します。しかし、他のデータベーステクノロジで使っていた、いくつかのクエリをサポートしていません。

Entities Without a Filtered Property Are Never Returned by a Query

An index only contains entities that have every property referred to by the index. If an entity does not have a property referred to by an index, the entity will not appear in the index, and will never be a result for the query that uses the index.

インデックスはインデックスによって参照される全てのプロパティを持つエンティティのみ保持します。もしエンティティがインデックスによって参照されるプロパティを持っていない場合、そのエンティティはインデックス内に現れません。そして、インデックスを使用したクエリの結果に決してなりません。

Note that the App Engine datastore makes a distinction between an entity that does not possess a property and an entity that possesses the property with a null value (null). If you want every entity of a kind to be a potential result for a query, you can use a JDO or JPA data class, which always assigns a value to every property that corresponds to a field in the class.

App Engine datastoreはプロパティ自体を保持していないエンティティと、プロパティは存在するがnull値であるプロパティを識別します。もしあなたの欲している、ある種の全てのエンティティをクエリの結果にしたい場合、クラスのフィールドに対応する全てのプロパティを値に常にアサインするJDOとJPAデータクラスを使うことができます。

Properties that Aren't Indexed

Property values that aren't indexed are not findable by queries. This includes properties that are marked as not indexed, as well as properties with values of the long text value type (Text) or the long binary value type (Blob).

インデクシングされないプロパティの値は、クエリによって見つけることができません。これにはインデックスしないとマークされたプロパティと、長いテキストの型(Text)や長い名バイナリの型(Blob)の値をもつプロパティが含まれます。

A query with a filter or sort order on a property will never match an entity whose value for the property is a Text or Blob, or which was written with that property marked as not indexed. Properties with such values behave as if the property is not set with regard to query filters and sort orders.

TextとBlobのプロパティ、もしくはインデックスしないようにマークされたプロパティをクエリのフィルタやソート順に指定した場合、そのエンティティは決してマッチしません。それらのようなプロパティは、プロパティがセットされていないように振る舞います。

Property Values of Mixed Types are Ordered By Type

When two entities have properties of the same name but of different value types, an index of the property sorts the entities first by value type, then by an order appropriate to the type. For example, if two entities each have a property named "age," one with an integer value and one with a string value, the entity with the integer value will always appear before the entity with the string value when sorted by the "Age" property, regardless of the values themselves.

2つのエンティティが同じ名前だが異なる型であるプロパティをもつとき、プロパティのインデックスは最初に値の型でソートし、次に型に適切にソートします。例えば、2つのエンティティが"age"という名前のプロパティを持ち、一つは整数の値をもち、もう一方は文字列の値を持つ場合、”Age"プロパティでソートするとその値を意識せずに整数の値をもつエンティティは常に文字列の値を持つエンティティの前に現れます。

This is especially worth noting in the case of integers and floating point numbers, which are treated as separate types by the datastore. A property with the integer value 38 is sorted before a property with the floating point value 37.5, because all integers are sorted before floats.

これは、Datastore内で別の型として扱われる、整数と浮動小数点の場合に、特に注目すべきことです。整数の値、38を持つプロパティが、浮動小数点の37.5の前にソートされます。なぜなら全ての整数型は浮動小数点の前にソートされるからです。

(If you're using JDO or JPA, this situation does not arise unless you modify a field's type without updating existing entities in the datastore, or use the low-level datastore API or a non-Java API.)

(もしJDOかJPAを使っている場合、このシチュエーションはDatastoreに存在するエンティティのフィールドの型を変更したり、低レベルDatastoreAPいや非JavaAPIを使わない限り発生しません。)

Defining Indexes With Configuration

App Engine builds indexes for several simple queries by default. For other queries, the application must specify the indexes it needs in a configuration file named datastore-indexes.xml. If the application running under App Engine tries to perform a query for which there is no corresponding index (either provided by default or described in datastore-indexes.xml), the query will fail.

App Engineはデフォルトでいくつかのシンプルなクエリ用にインデックスを構築します。他のクエリに対しては、アプリケーションは datastore-indexes.xmlという名前の設定ファイルでインデックスを指定しなければなりません。もしApp Engineで動作しているアプリケーションが、適切なインデックスがない(デフォルトかまたはdatastore-indexes.xmlで提供されない)クエリを実行すると、そのクエリは失敗します。

App Engine provides automatic indexes for the following forms of queries:

App Engineは下記の形のクエリのために自動的にインデックスを提供します。

  • queries using only equality and ancestor filters
  • queries using only inequality filters (which can only be of a single property)
  • queries with only one sort order on a property, either ascending or descending
  • 等号と祖先フィルタのみを使ったクエリ
  • 不等号フィルタ(一つのプロパティに)のみ使ったクエリ
  • (昇順、降順問わず)一つのソート順のみを使ったクエリ

Other forms of queries require their indexes to be specified in datastore-indexes.xml, including:

他の形のクエリはdatastore-indexes.xmlでインデックスを指定する必要があります

  • queries with multiple sort orders
  • queries with a sort order on keys in descending order
  • queries with one or more inequality filters on a property and one or more equality filters over other properties
  • queries with inequality filters and ancestor filters
  • 複数のソート順を指定したクエリ
  • キーに降順のソート順を指定したクエリ
  • 1つ以上の不等号フィルタをプロパティに指定し、一つ以上の等号フィルタを別のプロパティに指定したクエリ
  • 不等号フィルタと祖先フィルタを使用したクエリ

The development web server makes managing index configuration easy: Instead of failing to execute a query that does not have an index and requires it, the development web server can generate configuration for an index that would allow the query to succeed. If your local testing of your application calls every possible query the application will make (every combination of kind, ancestor, filter and sort order), the generated entries will represent a complete set of indexes. If your testing might not exercise every possible query form, you can review and adjust the index configuration before uploading the application.

開発用Webサーバでは、インデックスの設定を簡単に管理します。インデックスのないクエリの実行を失敗させる代わりに、開発用Webサーバはクエリが成功するようなインデックスの設定を生成することができます。もしあなたのアプリケーションをローカルテストで呼び出すとアプリケーションで発生する全ての可能性のあるクエリが作られ(全ての種類の組み合わせ、祖先、フィルタとソート順)、生成されたエントリは完全なインデックスのセットとして表されます。もし、テストが全ての可能性のあるクエリの形をテストしていない場合、アプリケーションをアップロードする前にインデックスを見直して調整することができます。

You can define indexes manually using a configuration file named datastore-indexes.xml in the WEB-INF/ directory of your application's WAR. If you have automatic index configuration enabled (see below), the development server creates an index configuration in a file named datastore-indexes-auto.xml in a directory named WEB-INF/appengine-generated/, and uses both files to determine the full set of indexes.

あなたのアプリケーションのWARディレクトリにあるWEB-INF/にあるdatastore-indexes.xmlという名前の設定ファイルを使ってインデックスを手動で定義することもできます。もし自動的インデックス設定を有効にしていると(下記参照)、開発サーバはインデックス設定をWEB-INF/appengine-generated/というディレクトリの中にある、datastore-indexes-auto.xmlというファイルに作成し、どちらのファイルもインデックスのフルセットを決定するのに使用します。

Consider once again the following example query:

次のクエリについて再度考えて見ましょう。

select from Person where lastName = 'Smith'
                      && height < 72
                   order by height desc

The configuration for the index needed by this query would appear in datastore-indexes.xml as follows:

このクエリのインデックスに必要な設定はdatastore-indexes.xmlにあらわれます。

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes
  xmlns="http://appengine.google.com/ns/datastore-indexes/1.0"
  autoGenerate="true">
    <datastore-index kind="Person" ancestor="false">
        <property name="lastName" direction="asc" />
        <property name="height" direction="desc" />
    </datastore-index>

</datastore-indexes>

If the <datastore-indexes> element in datastore-indexes.xml has the attribute autoGenerate="true" (as above) or if the app does not have a datastore-indexes.xml file, automatic index configuration is enabled. With automatic index configuration enabled, if the app performs this query in the development server and no configuration for the index exists, the server adds this index configuration to the datastore-indexes-auto.xml file.

もし、datastore-indexes.xmlファイルの要素にautoGenerate="true"属性がある場合、またはdatastore-indexes.xmlファイルが存在しない場合に、自動インデックス設定が有効になります。自動インデックス設定が有効な場合、アプリケーションは、開発サーバ上でかつインデックスの設定が存在しない場合に、このクエリに対して、datastore-indexes-auto.xmlファイルにインデックスの設定を追加します。

For more information on datastore-indexes.xml and datastore-indexes-auto.xml, see Java Datastore Index Configuration.

datastore-indexes.xmlとdatastore-indexes-auto.xmlについての詳細はJava Datastore Index Configurationを参照してください。

Queries on Keys

Entity keys can be the subject of a query filter or sort order. In JDO, you refer to the entity key in the query using the primary key field of the object. The datastore considers the complete key value for such queries, including the entity's parent path, the kind, and the app-assigned key name string or system-assigned numeric ID.

エンティティーのキーはクエリフィルタやソート順の対象とすることができます。JDOでは、オブジェクトのプライマリキーフィールドを使って、クエリのエンティティキーを参照します。Datastoreは、エンティティの親のパス、種類、アプリケーションで設定したキーの名前文字列またはシステムがアサインした数値のIDを含むクエリーを完全なキーと認識します。

Because an entity key is unique across all entities in the system, key queries make it easy to retrieve entities of a given kind in batches, such as for a batch dump of the contents of the datastore. Unlike JDOQL ranges, this works efficiently for any number of entities.

エンティティのキーはシステム内の全てのエンティティで一意であるため、キーによるクエリーはDatastoreのコンテンツのバッチダンプのようなバッチで、与えられた種類のエンティティを抽出することを簡単にします。JDOQLの範囲と異なり、これはどんな数のエンティティでも効率的に働きます。

Keys are ordered first by parent path, then by kind, then by key name or ID. Kinds and key names are strings and are ordered by byte value. IDs are integers and are ordered numerically. If entities of the same parent and kind use a mix of key name strings and numeric IDs, entities with numeric IDs are considered to be less than entities with key name strings. Elements of the parent path are compared similarly: by kind (string), then by key name (string) or ID (number).

キーは最初に親のパスによって順序付けられ、次に種類、次にキーの名前かIDとなります。種類とキーの名前は文字列で、バイト値によって順序付けられます。IDは整数で、数値的に順序付けられます。エンティティが同じ親と種類を持つ場合はキーの名前と数値IDの複合キーを使い、数値のIDをもつエンティティは名前文字列キーをもつエンティティより少ないと考えられます。親のエレメントパスは似たように、種類(String)、次にキー名(String)またはID(数値)の順で比較されます。

Queries involving keys use indexes just like queries involving properties, with one minor difference: Unlike with a property, a query with an equality filter on the key that also has additional filters must use a custom index defined in the app's index configuration file. As with all queries, the development web server creates appropriate configuration entries in this file when a query that needs a custom index is tested.

キーを使ったクエリは、プロパティを使ったクエリと同じくインデックスを使います。1つの小さな違いがあります。プロパティのときと異なり、キーに対する等号フィルタを持ち、追加のフィルタもまたもつクエリは、アプリケーションのインデックス設定ファイルにカスタムインデックス定義をしなければなりません。全てのクエリ同様に、カスタムインデックスが必要なクエリがテストされたときに、開発Webサーバは適切な設定を作成します。

Restrictions on Queries

The nature of the index query mechanism imposes a few restrictions on what a query can do.

このインデックスクエリのメカニズムの特質は、クエリが何ができるかに多少の制限を加えます。

Filtering Or Sorting On a Property Requires That the Property Exists

プロパティのフィルタやソートには、そのプロパティが存在する必要がある

A query filter condition or sort order for a property also implies a condition that the entity have a value for the property.

プロパティへのクエリフィルタの条件やソート順、エンティティの暗黙の条件もまたプロパティの値を持ちます。

A datastore entity is not required to have a value for a property that other entities of the same kind have. A filter on a property can only match an entity with a value for the property. Entities without a value for a property used in a filter or sort order are omitted from the index built for the query.

Datastoreのエンティティは、同種のほかのエンティティがもっているプロパティの値を必要としません。プロパティへのフィルタはプロパティの値を持っているエンティティにのみマッチすることができます。プロパティの値を持っていないエンティティはフィルタやソート順で使われると、そのクエリのインデックス構築から取り除かれます。

No Filter That Matches Entities That Do Not Have a Property

プロパティをもたないエンティティにフィルタはマッチしません。

It is not possible to perform a query for entities that are missing a given property. One alternative is to create a fixed (modeled) property with a default value of null, then create a filter for entities with null as the property value.

与えられたプロパティが存在しないエンティティにクエリを実行することはできません。一つの代替手段としてはデフォルト値がnullの固定されたプロパティをつくり、プロパティの値がnullのエンティティのフィルタを作ることです。

Inequality Filters Are Allowed On One Property Only

不等号フィルタは一つのプロパティにのみ許されます。

A query may only use inequality filters (<, <=, >=, >) on one property across all of its filters.

クエリは一つのプロパティにのみ不等号フィルタ (<, <=, >=, >)を使うことができます。

For example, this query is allowed:

例えばこのクエリは許可されます。

select from Person where birthYear >= minBirthYearParam
                      && birthYear <= maxBirthYearParam

However, this query is not allowed, because it uses inequality filters on two different properties in the same query:

しかしこのクエリは許可されません。なぜなら、同じクエリで不等号フィルタを異なるプロパティに使用しているからです。

select from Person where birthYear >= minBirthYearParam
                      && height >= minHeightParam   // ERROR

Filters can combine equal (==) comparisons for different properties in the same query, including queries with one or more inequality conditions on a property. This is allowed:

同じクエリで、不等号フィルタを使っていても、等号ならば異なるフィルタに使うことができます。これは許可されます。

select from Person where lastName == lastNameParam
                      && city == cityParam
                      && birthYear >= minBirthYearParam

The query mechanism relies on all results for a query to be adjacent to one another in the index table, to avoid having to scan the entire table for results. A single index table cannot represent multiple inequality filters on multiple properties while maintaining that all results are consecutive in the table.

クエリのメカニズムは、テーブル全体をスキャンしてしまうことを避けるために、インデックステーブルでお互い隣接するようにクエリの全ての結果を信頼します。一つのインデックステーブルは、テーブルの全ての結果が連続して並ぶように管理するために、複数のプロパティに対する複数の不等号フィルタを表すことができません。

Properties In Inequality Filters Must Be Sorted Before Other Sort Orders

不等号フィルタのプロパティは、他のソート順の前にソートされなければならない。

If a query has both a filter with an inequality comparison and one or more sort orders, the query must include a sort order for the property used in the inequality, and the sort order must appear before sort orders on other properties.

クエリが不等号フィルタと1つ以上のソート順を持つ場合、そのクエリは不等号フィルタで使ったプロパティのソート順を含めなければならず、そのソート順はほかのプロパティのソート順の前に現れなければなりません。

This query is not valid, because it uses an inequality filter and does not order by the filtered property:

このクエリは不正です。なぜなら不等号フィルタを使っており、そのフィルタプロパティでソートしていないからです。

select from Person where birthYear >= minBirthYearParam
                order by lastName                    // ERROR

Similarly, this query is not valid because it does not order by the filtered property before ordering by other properties:

同様に、このクエリも不正です。なぜなら、フィルタされたプロパティのソートの前に、他のプロパティのソートをしているからです。

select from Person where birthYear > minBirthYearParam
                order by lastName, birthYear         // ERROR

This query is valid:

このクエリは有効です。

select from Person where birthYear >= minBirthYearParam
                order by birthYear, lastName

To get all results that match an inequality filter, a query scans the index table for the first matching row, then returns all consecutive results until it finds a row that doesn't match. For the consecutive rows to represent the complete result set, the rows must be ordered by the inequality filter before other sort orders.

不等号フィルタにマッチした全ての結果を得るために、クエリはインデックステーブルをスキャンし、最初にマッチする行を探します。次に行がマッチしなくなるまで、連続する結果を返します。連続した行が完全な結果セットを表すために、その行は他のソート順で並び替える前に不等号フィルタによってソートしなければなりません。

Sort Orders and Properties With Multiple Values

Due to the way properties with multiple values are indexed, the sort order for these properties is unusual:

複数の値を持つプロパティをインデクシングするため、これらのプロパティのソート順は通常と異なります。

  • If the entities are sorted by a multi-valued property in ascending order, the value used for ordering is the smallest value.
  • If the entities are sorted by a multi-valued property in descending order, the value used for ordering is the greatest value.
  • Other values do not affect the sort order, nor does the number of values.
  • In the case of a tie, the key of the entity is used as the tie-breaker.
  • エンティティが複数の値を持つプロパティの昇順でソートされた場合、その値は小さい値から順序付けられます。
  • エンティティが複数の値を持つプロパティの降順でソートされた場合、その値は大きい値から順序付けられます。
  • 他の値はソート順に影響しません、数値の値もです。
  • 等しい場合は、エンティティのキーがタイブレーカーとして使われます。

This sort order has the unusual consequence that [1, 9] comes before [4, 5, 6, 7] in both ascending and descending order.

このソート順は、通常とはことなる順序をもちます。[1, 9]は昇順でも、降順でも[4,5,6,7]の前に来ます。

One important caveat is queries with both an equality filter and a sort order on a multi-valued property. In those queries, the sort order is disregarded. For single-valued properties, this is a simple optimization. Every result would have the same value for the property, so the results do not need to be sorted further.

重要な警告の一つは、等号フィルタとソート順の両方を、複数の値を持つプロパティに適用したクエリです。これらのクエリはソート順が無視されます。単一値のプロパティのため、最適化されます。各結果はプロパティと同じ値を持つため、その結果はこれ以上ソートする必要がありません。

However, multi-valued properties may have additional values. Since the sort order is disregarded, the query results may be returned in a different order than if the sort order were applied. (Restoring the dropped sort order would be expensive and require extra indices, and this use case is rare, so the query planner leaves it off.)

しかし、複数の値のプロパティは追加の値を持ちます。ソート順が無視されるため、クエリの結果はソート順が適用された場合と異なる順で返されるかもしれません。(落とされたソート順を復元するのは高価になり、追加のインデックスが必要になります。そして、この使い方はめったにないために、クエリプランナはそのままにしています。)

Big Entities and Exploding Indexes

As described above, every property (that doesn't have a Text or Blob value) of every entity is added to at least one index table, including a simple index provided by default, and any indexes described in the application's datastore-indexes.xml file that refer to the property. For an entity that has one value for each property, App Engine stores a property value once in its simple index, and once for each time the property is referred to in a custom index. Each of these index entries must be updated every time the value of the property changes, so the more indexes that refer to the property, the more time it will take to update the property.

上記のとおり、各エンティティの各プロパティ(TextとBlob値を除く)は、少なくとも一つのインデックステーブルに追加されます。インデックステーブルには、デフォルトで提供される単純なインデックス、そしてプロパティを参照するための、アプリケーションのdatastore-indexes.xmlファイルに記載されたインデックスがあります。全てのプロパティが単一の値をもつエンティティのため、AppEngineはプロパティの値を、シンプルインデックスにいったん格納します。そして、そのプロパティが参照されるカスタムインデックスに格納します。それらのインデックスのエンティティはプロパティの値が変更されるたび更新されなければなりません。よってインデックスが参照するプロパティが多くなると、プロパティの更新により時間がかかるようになります。

To prevent the update of an entity from taking too long, the datastore limits the number of index entries that a single entity can have. The limit is large, and most applications will not notice. However, there are some circumstances where you might encounter the limit. For example, an entity with very many single-value properties can exceed the index entry limit.

エンティティの更新が長くなりすぎないように、Datastoreは単一のエンティティが持つことのできる、インデックスのエンティティの数を制限します。その制限は大きく、ほとんどのアプリケーションは気付くことはないでしょう。しかし、いくつかの場合には制限に達することもあります。例えば、エンティティがとてもたくさんの単一値のプロパティをもっている場合、インデックスのエントリの制限を越えるかもしれません。

Properties with multiple values store each value as a separate entry in an index. An entity with a single property with very many values can exceed the index entry limit.

複数の値をとるプロパティはそれぞれの値が別のエントリとしてインデックスに保存されます。とても数多くの値をもつ1つのプロパティをもつエンティティはインデックスエントリの制限を超過する可能性があります。

Custom indexes that refer to multiple properties with multiple values can get very large with only a few values. To completely record such properties, the index table must include a row for every permutation of the values of every property for the index.

複数の値を持つ複数のプロパティを参照するカスタムインデックスは、少ない値でとても大きくなります。そのようなプロパティを完全に記録するため、インデックステーブルは、各プロパティの値の各順列の行を保持しなければなりません。

For example, the following index (described in datastore-indexes.xml syntax) includes the x and y properties for entities of the kind MyModel:

例えば、次のインデックス(datastore-indexes.xmlの文法で記載しています)は、MuModel種のエンティティでxとyのプロパティをもっています。

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes>
    <datastore-index kind="MyModel">
        <property name="x" direction="asc" />
        <property name="y" direction="asc" />
    </datastore-index>

</datastore-indexes>

The following code creates an entity with 2 values for the property x and 2 values for the property y:

次のコードはプロパティxに2つの値、プロパティyにも2つの値を持つエンティティを作成します。 

        MyModel m = new MyModel();

        m.setX(Arrays.asList("one", "two"));
        m.setY(Arrays.asList("three", "four"));

        pm.makePersistent(m);

To accurately represent these values, the index must store 12 property values: 2 each for the built-in indexes on x and y, and 2 for each of the 4 permutations of x and y in the custom index. With many values of multi-valued properties, this can mean an index must store very many index entries for a single entity. You could call an index that refers to multiple properties with multiple values an "exploding index," because it can get very large with just a few values.

これらの値を正確に表現するため、インデックスは12のプロパティの値を格納しなければなりません。xとyの組み込みインデックスのために2つ、カスタムインデックスにxとyの各順列4つを2つです。複数の値をもつプロパティがたくさんの値を持つとき、単一のエンティティのためにとてもたくさんのプロパティをインデックスに保持しなければならないことを意味します。複数の値を持つ複数のプロパティを参照するインデックスを「爆発したインデックス」と呼びます。なぜなら、少しの値のために非常に大きくなるためです。

If a put() would result in a number of index entries that exceeds the limit, the call will fail with an exception. If you create a new index that would contain a number of index entries that exceeds the limit for any entity when built, queries against the index will fail, and the index will appear in the "Error" state in the Admin Console.

put()の結果、インデックスのエントリの制限を超えた場合、その呼び出しは失敗し例外になります。エンティティを作成するときに、インデックスエントリの制限を越えた新しいインデックスを作ると、そのインデックスに対するクエリは失敗し、そのインデックスはAdminConsoleに「エラー」状態として表示されます。

You can avoid exploding indexes by avoiding queries that would require a custom index using a list property. As described above, this includes queries with multiple sort orders, a mix of equality and inequality filters, and ancestor filters.

リストプロパティを使う、カスタムインデックスを必要とするクエリを避けることによって、インデックスの爆発を防ぐことができます。上記の通り、これには複数のソート順、等号と不等号を混在したフィルタ、祖先フィルタを含みます。

2009/05/09

Creating, Getting and Deleting Data

つづき

キーも良くわからんなぁ。

Creating, Getting and Deleting Data

Saving a JDO data object to the datastore is a simple matter of calling the makePersistent() method of the PersistenceManager instance. The App Engine JDO implementation uses the primary key field of the object to keep track of which datastore entity corresponds with the data object, and can generate keys for new objects automatically. You can use keys to retrieve entities quickly, and you can construct keys from known values (such as account IDs).

JDOデータオブジェクトをDatastoreに保存するには、単純にPersistenceManagerインスタンスの makePersistent()メソッドを呼ぶだけです。App Engine JDO実装はデータオブジェクトと、対応するDatastoreのエンティティを記録するために、そのオブジェクトのプライマリキーのフィールドを利用します。新しいオブジェクトの場合、自動的にキーを生成することができます。エンティティをすばやく取得するためにキーを利用することができ、(アカウントのIDなど)既知の値からキーを生成することもできます。

Making Objects Persistent

To store a simple data object in the datastore, you call the PersistenceManager's makePersistent() method, passing it the instance.

データオブジェクトをDatastoreに保存するには、そのインスタンスを渡して、PersistenceManagerの makePersistent()を呼んでください。

        PersistenceManager pm = PMF.get().getPersistenceManager();

        Employee e = new Employee("Alfred", "Smith", new Date());

        try {
            pm.makePersistent(e);
        } finally {
            pm.close();
        }

The call to makePersistent() is synchronous, and doesn't return until the object is saved and indexes are updated.

makePersistent()の呼び出しは同期的で、オブジェクトが保存されインデックスが更新されるまで返ってきません。

To save multiple objects in JDO, call the makePersistentAll(...) method with a Collection of objects. In the current release, this is implemented similarly to a series of calls to makePersistent(). In a future release, this method will perform more efficient batch calls to the datastore.

JDOで複数のオブジェクトを保存するには、オブジェクトのコレクションを引数に、makePersistentAll(...)を呼んでください。現在のリリースでは、makePersistent()をそれぞれに呼ぶのと同様に実装されていますが、このメソッドは今後のリリースでより効率的にDatastoreのバッチ呼び出しとして実行されます。

Note: If any of the data object's persistent fields are references to other persistent data objects, and any of those objects have never been saved or have changed since they were loaded, the referenced objects will also be saved to the datastore. See Relationships.

注:対象のデータオブジェクトの永続化フィールドが、他の永続化データオブジェクトを参照しているときに、それらのオブジェクトが一度も保存されていない、または読み込まれたあと変更されている場合は、その参照されているオブジェクトもまたDatastoreに保存されます。Relationshipsを参照してください。

Keys

Every entity has a key that is unique over all entities in App Engine. A complete key includes several pieces of information, including the application ID, the kind, and an entity ID. (Keys also contain information about entity groups; see Transactions for more information.)

各エンティティはApp Engineの全てのエンティティで一意なキーをもっています。完全なキーは、そのアプリケーションのID、種類、エンティティIDなどを含む、いくつかの情報を持っています。(また、キーにはエンティティグループの情報を保持しています。詳細はTransactionsを参照してください。)

An object's key is stored in a field on the instance. You identify the primary key field using the @PrimaryKey annotation.

オブジェクトのキーは、そのインスタンスのフィールドに格納されます。@PrimaryKeyアノテーションを使用して、プライマリキーのフィールドを特定します。

The app can provide the ID portion of the key as a string when the object is created, or it can allow the datastore to generate a numeric ID automatically. The complete key must be unique across all entities in the datastore. In other words, an object must have an ID that is unique across all objects of the same kind and with the same entity group parent (if any). You select the desired behavior of the key using the type of the field and annotations.

アプリケーションは、オブジェクトが作られるときに、キーのIDの一部を文字列として渡すことができます。もしくは、Datastoreに自動的に数値のIDを生成させることもできます。完全なキーは Datastore内の全てのエンティティで一意である必要があります。言い換えると、オブジェクトは、同種の全てのオブジェクトで一意なIDと、(もし可能なら)同一のエンティティグループの親をもっていなければなりません。フィールドの型とアノテーションを利用して、望むようなキーの振る舞いを選択することができます。

If the class is used as a "child" class in a relationship, the key field must be of a type capable of representing an entity group parent: either a Key instance, or a Key value encoded as a string. See Transactions for more information on entity groups, and Relationships for more information on relationships.

そのクラスがリレーションシップ内で「子」クラスとして使われる場合、キーのフィールドはエンティティグループの親を表すことができるタイプでなければいけません。つまり、Keyのインスタンスか、Keyの値を文字列としてエンコードしたものです。エンティティグループの詳細はTransactions、リレーションシップの詳細はRelationshipsを参照してください。

Tip: If the app creates a new object and gives it the same string ID as another object of the same kind (and the same entity group parent), saving the new object overwrites the other object in the datastore. To detect whether a string ID is already in use prior to creating a new object, you can use a transaction to attempt to get an entity with a given ID, then create one if it doesn't exist. See Transactions.

Tip: もしアプリケーションが新しいオブジェクトを作成し、同種のほかのオブジェクト(そして同じエンティティグループの親)と同じ文字列のIDを渡した場合、その新しいオブジェクトはDatastore内のほかのオブジェクトを上書きします。新しいオブジェクトを作成すル前に、ある文字列のIDが既に使われていないか検出するには、トランザクションを利用して、そのIDでエンティティを検索し、存在しない場合にそのIDを使ってオブジェクトを作成してください。Transactionsを参照。

There are 4 types of primary key fields:

プライマリキーのフィールドには4つの種類があります。

Long

A long integer (java.lang.Long), an entity ID automatically generated by the datastore. Use this for objects without entity group parents whose IDs should be generated automatically by the datastore. The long key field of an instance is populated when the instance is saved.

Long Integer (java.lang.Long) エンティティIDはDatastoreによって自動的に生成されます。これは、Datastoreによって自動生成されるべきIDのエンティティグループの親をもたないオブジェクトに使用してください。インスタンスのLongキーのフィールドはそのインスタンスが保存されるときに設定されます。

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
Unencoded String

A string (java.lang.String), an entity ID ("key name") provided by the application when the object is created. Use this for objects without entity group parents whose IDs should be provided by the application. The application sets this field to the desired ID prior to saving.

文字列 (java.lang.String)、オブジェクトが作成されるときに、アプリケーションによって提供されるエンティティID ("key name")。アプリケーションによって提供されるべきIDのエンティティグループの親をもたないオブジェクトに使用してください。アプリケーションは保存する前に、希望するIDをこのフィールドにセットします。

import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    private String name;
Key

A Key instance (com.google.appengine.api.datastore.Key). The key value includes the key of the entity group parent (if any) and either the app-assigned string ID or the system-generated numeric ID. To create the object with an app-assigned string ID, you create the Key value with the ID and set the field to the value. To create the object with a system-assigned numeric ID, you leave the key field null. (For information on how to use entity group parents, see Transactions.)

Keyインスタンス(com.google.appengine.api.datastore.Key)。そのキーの値は、(もしあれば)エンティティグループの親のキーと、アプリケーションでアサインした文字列のIDかシステムで生成した数値のIDを含みます。アプリケーションがアサインした文字列のIDを使ってオブジェクトを作成するには、そのIDでKeyの値を生成し、その値をフィールドにセットしてください。システムがアサインした数値のIDでオブジェクトを生成する場合、キーフィールドをNullのままにしてください。(エンティティグループの親の使い方に関する情報は、Transactionsを参照してください。)

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;

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

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

The app can create a Key instance using the KeyFactory class:

アプリケーションはKeyFactoryクラスを利用して、Keyのインスタンスを生成することができます。

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

// ...
        Key key = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = new Employee();
        e.setKey(key);
        pm.makePersistent(e);
Key as Encoded String

Similar to Key, but the value is the encoded string form of the key. Encoded string keys allow you to write your application in a portable manner and still take advantage of App Engine datastore entity groups.

Keyと似ていますが、その値はキーをエンコードした文字列の形です。Encoded Stringのキーは、移植性の高いアプリケーションを作成できるようにしますが、その利点はApp Engine datastoreでもあります。

import javax.jdo.annotations.Extension;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

The app can populate this value prior to saving using a key with a name, or it can leave it null. If the encoded key field is null, the field is populated with a system-generated key when the object is saved.

アプリケーションは、キーと名前を利用して、保存する前にこの値を設定することもできますし、Nullのままにしておくこともできます。このフィールドがNullだった場合、このオブジェクトが保存されるときにシステムで生成されたキーが設定されます。

Key instances can be converted to and from the encoded string representation using the KeyFactory methods keyToString() and stringToKey().

KeyインスタンスはKeyFactoryのメソッド、 keyToString() とstringToKey()を使って、エンコードされた文字列表現にしたり、復元したりすることができます。

When using encoded key strings, you can provide access to an object's string or numeric ID with an additional field:

エンコードされたキーの文字列を利用する場合、追加のフィールドでオブジェクトの文字列または数値のIDにアクセスできるようにする必要があります。

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

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

    // OR:

    @Extension(vendorName="datanucleus", key="gae.pk-id", value="true")
    private Long keyId;

A "gae.pk-name" field can be set to a key name prior to saving the object. When the object is saved, the encoded key field is populated with the complete key that includes the key name. Its type must be String.

"gae.pk-name"フィールドはオブジェクトを保存する前にキー名を設定することができます。オブジェクトが保存されると、エンコードされたキーのフィールドは、キー名を含む完全なキーが設定されます。その型はStringでなければなりません。

A "gae.pk-id" field is populated when the object is saved, and cannot be modified. Its type must be Long.

"gae.pk-id"フィールドは、オブジェクトが保存されたときに設定されます。そして変更することはできません。これはLong型でなければなりません。

When a new object with a generated key (a key field using valueStrategy = IdGeneratorStrategy.IDENTITY) is created, its key value starts out null. The key field is populated when the object is written to the datastore. If you are using a transaction, the object is written when the transaction is committed. Otherwise, the object is written when the makePersistent() method is called if the object is being created, or when the PersistenceManager instance's close() method is called if the object is being updated.

生成されたキー(キーのフィールドにはvalueStrategy = IdGeneratorStrategy.IDENTITYを使用)とともに新しいオブジェクトが生成されたとき、そのキーの値は最初はNullです。このキーのフィールドはオブジェクトがDatastoreに書き込まれたときに設定されます。もし、トランザクションを利用している場合は、そのオブジェクトはトランザクションがコミットされたときに書き込まれます。その他の場合、そのオブジェクトが生成されたときは makePersistent()が呼ばれた場合、またオブジェクトが更新されるときはPersistenceManagerのインスタンスの close()メソッドが呼ばれた場合に設定されます。

Creating and Using Keys

If the application knows every element of an entity's complete key, the application can create the corresponding Key object without the object.

アプリケーションがエンティティの完全なキーの各要素を知っている場合、アプリケーションは対応するKeyオブジェクトを、そのオブジェクトなしで生成することができます。

For a key for an entity without an entity group parent, you can use the createKey() static method of the KeyFactory class. This method takes a kind (the simple name of the class) and either an app-assigned string ID or a system-assigned numeric ID, and returns a Key object. For example, to recreate the key of an entity of kind "Employee" with key name "Alfred.Smith@example.com" (and no entity group parent):

エンティティグループの親のない場合のキーは、 KeyFactoryクラスのスタティックメソッドである、 createKey()を使えます。このメソッドは種類(クラスのシンプルネーム)とアプリケーションの設定した文字列IDまたはシステムの設定した数値IDを引数にとり、Keyオブジェクトを返します。たとえば、エンティティーの種類"Employee"とキー名"Alfred.Smith@example.com"(そしてエンティティグループの親がない)でキーを再生成します。

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

// ...
        Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");

To recreate a key of an entity of kind "Employee" with a system-assigned numeric ID of 52234 (and no entity group parent):

エンティティの種類が"Employee"で、システムが設定した数値IDが52234(かつエンティティグループの親がない)のキーを再生成するには

        Key k = KeyFactory.createKey(Employee.class.getSimpleName(), 52234);

Keys can be converted to and from a string representation using the KeyFactory class's keyToString() and stringToKey() methods, respectively. (Note that this is different from the Key class's toString() method, which returns a human-readable value suitable for debugging.)

キーはKeyFactoryクラスのkeyToString() とstringToKey()で文字列と相互に変換できます。(KeyクラスのtoString()とは異なることに注意してください。こちらはデバッグに適した人間が読むための文字列を返します。)

For a key for an entity with an entity group parent, you can use the KeyFactory.Builder class:

エンティティとエンティティグループの親のためのキーには、KeyFactory.Builderクラスを使用してください。

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

// ...
        Key k = new KeyFactory.Builder(Employee.class.getSimpleName(), 52234).addChild(ExpenseReport.class.getSimpleName(), "A23Z79").getKey();

The Builder instance's addChild() method returns the Builder, so you can chain calls to add each element of the key path. To get the complete Key value for a given builder, you call the Builder's getKey() method.

BuilderインスタンスのaddChild()メソッドはBuilderを返します。よって、キーのパスの要素を追加するためにチェインコールができます。完全なKeyの値を得るために、BuilderのgetKey()メソッドを呼んでください。

For more information about entity groups, see Transactions.

エンティティグループの詳細については、Transactionsを参照して下さい。

Getting an Object By Key

To retrieve an object given its key, use the PersistenceManager's getObjectById() method. The method takes the class for the object, and key:

キーからオブジェクトを取得するには、PersistenceManagerの getObjectById()メソッドを使います。このメソッドは、オブジェクトのクラスと、キーを引数にとります。

        Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = pm.getObjectById(Employee.class, k);

If the class uses a key field that is an unencoded string ID (String) or numeric ID (Long), getObjectByID() can take the simple value as the key parameter:

もしクラスがエンコードされていない文字列のID(String)または数値ID(Long)のキーフィールドを使っている場合、getObjectByID()はキーパラメータとして単純な値をとります。

        Employee e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");

The key argument can be of any of the supported key field types (string ID, numeric ID, Key value, encoded key string), and can be of a different type than the key field in the class. App Engine must be able to derive the complete key from the class name and the provided value. String IDs and numeric IDs are exclusive, so a call using a numeric ID will never return an entity with a string ID. If a Key value or encoded key string is used, the key must refer to an entity whose kind is represented by the class.

キーの引数は、サポートされているいかなるキーフィールドの型(文字列ID、数値ID、Key、エンコード化されたキー文字列)でもとることができます。そして、クラスのキーフィールドと異なる型も使えます。AppEngineはクラス名と与えられた値から完全なキーを導出できなければなりません。文字列IDと数値IDは排他的です。よって数値IDをつかって呼び出すと、文字列 IDのエンティティは決して戻りません。Key値またはエンコードされたキー文字列が使われている場合、そのキーは、クラスによって表される種類のエンティティを参照しなければなりません。

Updating an Object

One way to update an object with JDO is to fetch the object, then modify it while the PersistenceManager that returned the object is still open. Changes are persisted when the PersistenceManager is closed. For example:

JDOをつかってオブジェクトを更新する一つの方法は、PersistenceManagerがオブジェクトを戻して開いている間に変更することです。変更はPersistenceManagerが閉じられたときに保存されます。例えば

public void updateEmployeeTitle(User user, String newTitle) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        Employee e = pm.getObjectById(Employee.class, user.getEmail());
        if (titleChangeIsAuthorized(e, newTitle) {
            e.setTitle(newTitle);
        } else {
            throw new UnauthorizedTitleChangeException(e, newTitle);
        }
    } finally {
        pm.close();
    }
}

Since the Employee instance was returned by the PersistenceManager, the PersistenceManager knows about any modifications that are made to Persistent fields on the Employee and automatically updates the datastore with these modifications when the PersistenceManager is closed. It know this because the Employee instance is "attached" to the PersistenceManager.

EmployeeインスタンスがPersistenceManagerから戻されたとき、PersistenceManagerはEmployeeの永続化フィールドにされた変更をしっており、PersistenceManagerが閉じられたときにそれらの変更は自動的にDatastoreに反映されます。Employeeインスタンスは、PersistenceManagerに「アタッチ」されているからです。

You can modify an object after the PersistenceManager has been closed by declaring the class as "detachable." To do this, add the detachable attribute to the @PersistenceCapable annotation:

対象のクラスを"detachable"として宣言することにより、PersistenceManagerが閉じられてからオブジェクトを変更することができます。これを行うには@PersistenceCapableアノテーションにdetachable属性を付加します。

import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
public class Employee {
    // ...
}

Now you can read and write the fields of an Employee object after the PersistenceManager that loaded it has been closed. The following example illustrates how a detached object might be useful:

これで、 PersistenceManagerによってロードされたEmployeeオブジェクトのフィールドを、PersistenceManagerが閉じられたあとも、読み書きすることができます。次の例でデタッチされたオブジェクトがどのように役に立つかを表しています。

public Employee getEmployee(User user) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    pm.setDetachAllOnCommit(true);
    try {
        e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");
    } finally {
        pm.close();
    }
    return e;
}

public void updateEmployeeTitle(Employee e, String newTitle) {
    if (titleChangeIsAuthorized(e, newTitle) {
        e.setTitle(newTitle);
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistent(e);
        } finally {
            pm.close();
        }
    } else {
        throw new UnauthorizedTitleChangeException(e, newTitle);
    }
}

Detached objects are a nice alternative to creating data transfer objects. For more information on working with detached objects please see the DataNucleus documentation.

デタッチされたオブジェクトはDTOを作成する良い代替です。デタッチされたオブジェクトをどのように使うかについての情報はDataNucleus documentationを参照してください。

Deleting an Object

To delete an object from the datastore, call the PersistenceManager's deletePersistent() method with the object:

Datastoreからオブジェクトを削除するには、PersistenceManagerのdeletePersistent()メソッドに対象のオブジェクトを渡してよんでください

        pm.deletePersistent(e);

If an object has fields containing child objects that are also persistent, the child objects are also deleted. See Relationships for more information.

もし、オブジェクトが子オブジェクトをもっている場合、その子オブジェクトもまた削除されます。詳細はRelationshipsを参照してください。