Top Banner
App Engine for Java の JDO ののののののの のの のののののの の のの ()
26
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Gaej Jdo

App Engine for Java のJDO 使用上の考慮点

(株)ケーピーエス 平 克介

Page 2: Gaej Jdo

目次 RDB から JDO への実装の変更概要

今回トライした例について JDO の基本 考慮点 –  Transaction 考慮点 – 関連 考慮点 –  PersistenceManager 考慮点 –  Index

参考:基本情報技術者試験 午前問題特訓システム

Page 3: Gaej Jdo

KPS フレームワークの機能 クライアントから直接擬似 HibernateAPI を操作

ORM オブジェクトを検索、更新、作成、削除 クライアントからサーバー側 Sesar2 コンポーネン

トの呼び出し Gxt   GUI 画面デザインツール  D-RexGxt

Hibernate 部分をJDO へ入れ替え

Criteria を Queryへ変更

集計処理部分の 変更

Page 4: Gaej Jdo

JDO への移行 サーバー側の ORM アクセス部分の変更

Hibernate から JDO へ フレームワーク側の変更

クライアント側検索 API の変更 Criteria から Query へ

フレームワークおよびクライアントコードの変更 集計処理部分の変更

集計処理を行っている Seaser2 コンポーネントの変更

Page 5: Gaej Jdo

JDO の基本1 JDO とは

Java 標準のデータの永続化・検索機能を提供する API

JDO の ORM クラス( GAE では kind と呼ぶ )@PersistenceCapable(identityType = IdentityType.APPLICATION)

public class Employee {    @PrimaryKey    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)    private Long id;    @Persistent    private String firstName;    @Persistent    private String lastName; :

この下に constructorgetter/setter が続く

Page 6: Gaej Jdo

JDO の基本 2

Object(entity) を永続化する

        PersistenceManager pm = PMF.get().getPersistenceManager();        Employee e = new Employee("Alfred", "Smith", new Date());        try {            pm.makePersistent(e);        } finally {            pm.close();        }

Page 7: Gaej Jdo

JDO の基本 3 Object の更新

Object の削除

    PersistenceManager pm = PMF.get().getPersistenceManager();    try {        Employee e = pm.getObjectById(Employee.class, 1);         e.setTitle(newTitle);    } finally {        pm.close();    } :

        pm.deletePersistent(e);

       :

Page 8: Gaej Jdo

JDO の基本 4 Object の検索

    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();    } :

Where lastName=‘Smith’ Order by hireDate desc

に相当

Page 9: Gaej Jdo

JDO の基本 5 Transaction

        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();            }        }

Tx 間に他所で同じ entity が更新された場合

JDOOptimisticVerificationException となる

Page 10: Gaej Jdo

JDO の基本 6 com.google.appengine.api.datastore.Key

親のクラスを持つ場合は主キーに Key を使用する Key は親のクラスの Key への参照を持つ

子 Key→ 親 Key Entity の Key は変更できない

import com.google.appengine.api.datastore.Key;// ...    @PrimaryKey    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)    private Key key;    public void setKey(Key key) {        this.key = key;    } :

Page 11: Gaej Jdo

JDO の基本 7 関連

Employee.java

ContactInfo.java

Employee:Contactinfo = 1:N この場合 Employee が Contactinfo の親になる 共通の親(親の親も含む)を持つ entity の集合を entity

group と言う

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

// ...    @Persistent    private Employee employee;

Page 12: Gaej Jdo

参考資料 ORM クラス間の一般的な関連の実装方法

商品 CD 商品名 単価 商品メーカー CD

商品メーカー CD 商品メーカー名

商品テーブル

商品メーカーテーブル

主キー

RDBの場合:主キー /外部キーの関連

String shohinCdString shohinNm

Long tankaShohinMaker shohinMaker

Shohin

getShohinMaker()

String shohinMakerCdString shohinMakerNm

Set shohins

ShohinMaker

getShohins()

ORMでのオブジェクト間の関連

10..*

外部キー

関連相手の Objectの参照を持つ 関連相手の Object

の集合の参照を持つ

Page 13: Gaej Jdo

考慮点 (Transaction) 1 Transaction 内では同一の entity group に

属する entity しか扱えない

つまり、共通の親を持つ entity しか Transaction化できない

   tx.begin(); Query query = pm.newQuery(Employee.class); List<Employee> results = (List<Employee>) query.execute(); Employee e1 = results.get(0); Employee e2 = results.get(1); e1.setFirstName(e1.getFirstName() + "1"); e2.setFirstName(e2.getFirstName() + "2"); tx.commit(); javax.jdo.JDOException

Page 14: Gaej Jdo

考慮点 (Transaction) Transaction の制約を受けない方法

単一の entity 「 root 」をすべての親の無い entityの親にする

root

employee3 company2

contactInfo2

employee2

contactInfo1

employee1

company1

すべては同一の entity group

Page 15: Gaej Jdo

考慮点 (Transaction) Root クラス ( 主キーだけあればよい )

@PersistenceCapable(identityType = IdentityType.APPLICATION)public class Root { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; public Key getKey() { return key; } public void setKey(Key key) { this.key = key; }}

Page 16: Gaej Jdo

考慮点 (Transaction) 親の無い entity の例( Root の Key を持たせる )

Root との ManyToOne 関連を持たせる方法でも OK

@PersistenceCapable(identityType = IdentityType.APPLICATION)public class Employee { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent @Extension(vendorName="datanucleus", key="gae.parent-pk", value="true") private Key rootKey; public void setRootKey(Key rootKey) { this.rootKey = rootKey; }}

Page 17: Gaej Jdo

考慮点 (Transaction) 親が root である Entity の新規作成

root の key をセットする (root のキー値が1の場合 )

// ...Employee e = new Employee("Alfred", "Smith", new Date());Key key = KeyFactory.createKey("Root", 1);e.setRootKey(key);pm.makePersistent(e); :

Page 18: Gaej Jdo

考慮点 ( 関連 ) 関連実装時の問題(関連が Key の構造にな

る ) 親を一度設定すると変更できない 複数の親を定義できない

employee1

division1 division2

x

Employee1 は一生 division1

season1

question1

genre1

x

2009 年春の「コンピュータの基礎」の問題

Season と Genre 両方親に出来ない

Page 19: Gaej Jdo

考慮点 7 ( 関連 - 解決策 ) 関連は親の Key を保持することで実装する

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

@PrimaryKey@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)private Key key;@Persistentprivate Key seasonKey;@Persistentprivate Key genreKey;public Season getSeason() {

if (this.seasonKey == null) {return null;

}return getPm().getObjectById(Season.class, this.seasonKey);

}public void setSeason(Season season) {

this.seasonKey = season.getKey();} :

関連先 Object を要求された時に entity を読む

Page 20: Gaej Jdo

考慮点 (PersistenceManager) PersistenceManager の使いまわし

一連の処理で使用する PersistenceManager は同一のものを使用する必要がある。

public Season getSeason() { if (this.seasonKey == null) { return null; } return pm.getObjectById(Season.class, this.seasonKey);}

// ... Season s = question.getSeason(); s.setCnt(s.getCnt()+1); pm.makePersistent(s);

Page 21: Gaej Jdo

考慮点 (PersistenceManager) PersistenceManager を request に保持する

public class PMManager { public static final String pmName = " PMManager _PM"; public static PersistenceManager getPm() { HttpServletRequest request = null; PersistenceManager pm = null; try { request = (HttpServletRequest) SingletonS2ContainerFactory .getContainer().getComponent(HttpServletRequest.class); pm = (PersistenceManager) request.getAttribute(pmName); } catch (Exception e) { } if (pm == null) { pm = PMF.get().getPersistenceManager(); if (request != null) { request.setAttribute(pmName, pm); } } return pm; }}

この場合 Seasar2 のコンテナから request を得ている

Page 22: Gaej Jdo

考慮点 (PersistenceManager)

PMManager から入手する例

public Season getSeason() { if (this.seasonKey == null) { return null; } return PMManager.getPm() . getObjectById(Season.class, this.seasonKey);}

// ... Season s = question.getSeason(); s.setCnt(s.getCnt()+1); PMManager.getPm().makePersistent(s);

Page 23: Gaej Jdo

考慮点 (Index) 検索条件によっては明示的に Index を定義す

る必要がある。 Google の資料で Index 定義が必要な検索とは

複数のソートが指定された場合 Key の逆順ソートの場合 == でない条件と == の条件がある場合 == の条件と ancestor フィルターがある場合

Local 環境で OK でも Deploy した環境で NG の場合がある

Page 24: Gaej Jdo

考慮点 (Index) Index の設定例

war/WEB-INF/datastore-indexes.xml に指定

<?xml version="1.0" encoding="utf-8"?><datastore-indexes autoGenerate="false"> <datastore-index kind="Question" ancestor="false" source="auto"> <property name="seasonKey" direction="asc"/> <property name="genreKey" direction="asc"/> </datastore-index></datastore-indexes>

Page 25: Gaej Jdo

その他の考慮点

kind 間の Join による読み込みはできない group by を使用した集約関数は使用できない

count(*) / sum(xxx) 等 検索条件に  != 、 IN は使用できない

Python ではクライアント側の実装として実現している

検索条件の組み合わせで or 、 not は使用できない

Page 26: Gaej Jdo

おわり

ありがとうございました