Post on 24-Jan-2015
description
appengine java night #2実践 Low-Level API ~クエリ編~
shin1ogawa@株式会社トップゲート
自己紹介...shin1ogawaです!Google, Java, AppEngine, Eclipse, Wicket, Maven, Hudson, 構成管理... • 参加しているOSSプロダクトやコミュニティ
• [SF.jp]Jiemamy...データベースの進化的設計
• [SF.jp]gae-j-samples...GAE/Jのサンプル、GAE/J/Maven統合プラグイン
• [SF.jp]gaejtools...GAE/J用のユーティリティ(予定)
• [SF.jp]asclipse...Amateras AIR GEAR用のAS3構文解析ライブラリ
• java-ja, wicket-ja,
株式会社トップゲート(http://www.topgate.co.jp)で、GoogleAppEngineやGoogleAppsを使用したソリューションを提供する為のお仕事をしています。
Low-Level API#1の復習
復習) Key• KeyのみでEntityGroupが構成される• Entityの属性にEntityGroup的に子供の…とかそんなのはいっさい無い。
• Keyオブジェクトに保持されている情報• ApplicationID• 親EntityのKey, 自身のKind名• name文字列or自動採番されたlong値
• 文字列としてエンコードされた表現で保存される
復習) KeyFactory
• 何種類かのcreateKey()メソッドでKeyオブジェクトを生成できる
• keyToString()メソッドでKeyオブジェクトをエンコードされた文字列に変換できる。
• stringToKey()メソッドで、エンコードされた文字列からKeyオブジェクトに変換できる。
復習) Entity
• setProperty(“属性名”, Object 属性値)• Object getProperty(“属性名”)• boolean hasProperty(“属性名”)• <missing>を判断できる
• 必ずしも、設定した型で値が保存されているとは限らない• HashSet等のCollectionはArrayList• intはLong…
復習) DatastoreService
• 保存• Key put(Entity)• List<Key> put(Iterable<Entity>)
• 削除• void delete(Key... keys)• void delete(Iterable<Key>)
• 上記のメソッドはすべて、第一引数にTransactionを指定できる。
復習) DatastoreService
• キーによる取得• Entity get(Key key)• 存在しないKeyを指定した場合は EntityNotFoundExceptionが飛ぶ。
• Map<Key, Entity> get( Iterable<Key> key)• 存在しないKeyが含まれていた場合は、返り値のMapに要素が入ってこない。
復習) DatastoreService
• Transactionの開始,取得• beginTransaction()• getCurrentTransaction()• ...ほかにもいくつか。
• Transactionの操作• commit()• rollback()• isActive()
復習) DatastoreService
• 1.2.5より前ではKeyを自動生成する場合はEntityを保存した後にしかそのKeyを取得できなかったが、1.2.5からは明示的に自動採番済みのKeyを取得する事ができる。• KeyRange allocalteIds( [親Key], Kind, 確保する数)
• KeyRange• getStart(), getEnd(), iterator()
Datastore Service保存のサンプル(Entityの準備)KeyRange parentKeys = service.allocateIds("Parent", 1);Key parentKey = parentKeys.getStart();KeyRange childKeys = service.allocateIds(parentKey, "Child", 2);Iterator<Key> childKeysIterator = childKeys.iterator();Entity parent = new Entity(parentKey);Entity child1 = new Entity(childKeys.next());Entity child2 = new Entity(childKeys.next());
Datastore Service保存のサンプル(保存)
Transaction tx = service.beginTransaction();try { List<Entity> entities = Arrays.asList(parent, child1, child2 service.put(tx, entities); tx.commit();} finally { if (tx.isActive()) { tx.rollback(); }}
Low-Level APIここから#2の内容
Datastore ServiceQuery• new Query(String kind)• JDOっぽく、フィルタ条件やソート条件を指定する場合
• new Query(String kind, Key ancestorKey)• 指定したKindで、指定した親キーに属するエンティティを取得する。
• new Query(Key ancestorKey)• 指定した親キーに属するエンティティを、すべてのKindにまたがって、末端のエンティティまで取得する。• ローカル環境では動作しない、という不具合が残念!
Datastore ServiceQuery• フィルタ条件の指定• Query addFilter(String propertyName,
FilterOperator operator, Object value)• ソート条件の指定• Query addSort(String propertyName,
SortDirection direction)• 取得対象をキーのみにしぼる• Query setKeysOnly()• Keyにしかアクセスしないのでずいぶん早くなる。
主キーを意味する属性名は Entity.KEY_RESERVED_PROPERTY
Datastore ServicePreparedQuery• DatastoreService#prepare()メソッドにQueryオブジェクトを渡す事で、PreaparedQueryが取得できる。
• 件数を取得する• int countEntities()
• 一件だけ取得する• Entity asSingleResult()• 条件にマッチするエンティティが無ければnullが返る。• 複数のEntityが条件にマッチした場合は
TooManyResultsExceptionが投げられる。
Datastore ServicePreparedQuery• 複数件を取得する• List<Entity> asList(FetchOptions)• まとめてフェッチする事ができる。
• Iterator<Entity> asIterator([FetchOptions])• いわゆるカーソルのような操作になる。
• Iterable<Entity> asIterable([FetchOptions])• iterator()を取得した瞬間にDatastoreにアクセスする。そこから先はasIterator()と同じ動作。
• FetchOptions• フェッチのためのオプション。offset, limit等。
Datastore ServiceQueryのサンプルQuery query = new Query(“Entity”);query.addFilter(“name”, FilterOperator.GREATER_THAN_OR_EQUAL, “hoge”) .addSort("name", SortDirection.ASCENDING) .addSort("__key__", SortDirection.ASCENDING);
DatastoreService service = DatastoreServiceFactory.getDatastoreService();List<Entity> entities = service.prepare(query).asList( FetchOptions.Builder.withOffset(0).limit(100));
Datastore ServiceQueryのサンプル: EntityGroupの取得Iterator<Entity> entityGroup = service.prepare(new Query(rootKey) .addSort(“__key__”)).asIterator( FetchOptions.Builder.withOffset(0).limit(100));Parent parent = null;while (entityGroup.hasNext) { Entity entity = entityGroup.next(); if (entity.getKind().equals(“Parent”)) parent = EntityUtil.toBean(entity,Parent.class); else if (entity.getKind().equals(“Child”)) parent.getChidren().add( EntityUtil.toBean(entity, Child.class); ...
Datastore ServiceちょっとJDOに話を戻しますJDOでたまに話題になる話についてちょっと考察。
class MyEntity { List<Child> children1; List<Child> children2;}myEntity.getChildren1().addAll(childA, childB);myEntity.getChildren2().addAll(childC, childD);manager.makePersistent(myEntity);
上記のように、同じ型の別のListを属性として持っているエンティティがあったとした時。
Datastore ServiceちょっとJDOに話を戻しますMyEntity myEntity = manager.getObjectById(MyEntity.class, key);List<Child> children1 = myEntity.getChildren1();List<Child> children2 = myEntity.getChildren2();
この時のchildren1には、childAとchildBだけではなく、childCとchildDが格納されている。children2はnull。
Datastore ServiceちょっとJDOに話を戻します
MyEntiy(1)
MyEntiy(1)/Child(A)
MyEntiy(1)/Child(B)
MyEntiy(1)/Child(C)
MyEntiy(1)/Child(D)
当然、親子関係は正しい構成で保存されているが、Child(A-D)がそれぞれどの属性に保持されていたか?は知りようが無い!
このように、実装を意識する事でハマリは遭遇しにくかったり、ハマった時も理解できる。
ご清聴ありがとうございました!
shin1ogawa@株式会社トップゲート