Gaej Jdo
- 2. 目次
RDB から JDO への実装の変更概要
今回トライした例について
JDO の基本
考慮点 – Transaction
考慮点 – 関連
考慮点 – PersistenceManager
考慮点 – Index
参考:基本情報技術者試験 午前問題特訓システム
- 3. KPS フレームワークの機能
クライアントから直接擬似 HibernateAPI を操作
ORM オブジェクトを検索、更新、作成、削除
クライアントからサーバー側 Sesar2 コンポーネン
トの呼び出し
Gxt GUI 画面デザインツール D-RexGxt
Hibernate 部分を
JDO へ入れ替え
Criteria を Query 集計処理部分の
へ変更 変更
- 4. JDO への移行
サーバー側の ORM アクセス部分の変更
Hibernate から JDO へ
フレームワーク側の変更
クライアント側検索 API の変更
Criteria から Query へ
フレームワークおよびクライアントコードの変更
集計処理部分の変更
集計処理を行っている Seaser2 コンポーネントの
変更
- 5. 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; この下に constructor
: getter/setter が続く
- 6. JDO の基本 2
Object(entity) を永続化する
PersistenceManager pm = PMF.get().getPersistenceManager();
Employee e = new Employee("Alfred", "Smith", new Date());
try {
pm.makePersistent(e);
} finally {
pm.close();
}
:
- 7. JDO の基本 3
Object の更新
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
Employee e = pm.getObjectById(Employee.class, 1);
e.setTitle(newTitle);
} finally {
pm.close();
}
:
Object の削除
pm.deletePersistent(e);
:
- 8. 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 ... Where lastName=‘Smith’
} Order by hireDate desc
} finally { に相当
query.closeAll();
} :
- 9. 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 間に他所で同じ entity が更新された場
tx.rollback(); 合
} JDOOptimisticVerificationException とな
} る
:
- 10. JDO の基本 6
com.google.appengine.api.datastore.Key
import com.google.appengine.api.datastore.Key;
// ...
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
public void setKey(Key key) {
this.key = key;
} :
親のクラスを持つ場合は主キーに Key を使用する
Key は親のクラスの Key への参照を持つ
子 Key→ 親 Key
Entity の Key は変更できない
- 11. JDO の基本 7
関連
Employee.java
// ...
@Persistent(mappedBy = "employee")
private List<ContactInfo> contactInfoSets;
ContactInfo.java
// ...
@Persistent
private Employee employee;
Employee:Contactinfo = 1:N
この場合 Employee が Contactinfo の親になる
共通の親(親の親も含む)を持つ entity の集合を entity
group と言う
- 12. 参考資料
ORM クラス間の一般的な関連の実装方法
RDB の場合:主キー / 外部キーの関連
商品テーブル
外部キ
商品 CD 商品名 単価 商品メーカー CDー
商品メーカーテーブル
商品メーカー CD 商品メーカー名
関連相手の Object 主キー
の参照を持つ 関連相手の Object
の集合の参照を持つ
ORM でのオブジェクト間の関連
Shohin ShohinMaker
1
String shohinCd 0..* String shohinMakerCd
String shohinNm String shohinMakerNm
Long tanka Set shohins
ShohinMaker shohinMaker
getShohinMaker() getShohins()
- 13. 考慮点 (Transaction)
1 Transaction 内では同一の entity group に
属する entity しか扱えない
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
つまり、共通の親を持つ entity しか Transaction
化できない
- 14. 考慮点 (Transaction)
Transaction の制約を受けない方法
単一の entity 「 root 」をすべての親の無い entity
の親にする
root
employee1
employee2
employee3 company1
company2
contactInfo1
contactInfo2
すべては同一の entity group
- 15. 考慮点 (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;
}
}
- 16. 考慮点 (Transaction)
親の無い entity の例( Root の Key を持たせる )
@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;
}
}
Root との ManyToOne 関連を持たせる方法でも OK
- 17. 考慮点 (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);
:
- 18. 考慮点 ( 関連 )
関連実装時の問題(関連が Key の構造にな
る)
親を一度設定すると変更できない
複数の親を定義できない
division1 division2 2009 年春の「コンピュータの基礎」の問題
season1 genre1
x
employee1 x
Employee1 は一生 division1 question1
Season と Genre 両方親に出来ない
- 19. 考慮点 7 ( 関連 - 解決策 )
関連は親の Key を保持することで実装する
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Question {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private Key seasonKey;
@Persistent 関連先 Object を要求された時
private Key genreKey;
に entity を読む
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();
} :
- 20. 考慮点 (PersistenceManager)
PersistenceManager の使いまわし
一連の処理で使用する PersistenceManager は同
一のものを使用する必要がある。
// ...
Season s = question.getSeason();
s.setCnt(s.getCnt()+1);
pm.makePersistent(s);
public Season getSeason() {
if (this.seasonKey == null) {
return null;
}
return pm.getObjectById(Season.class, this.seasonKey);
}
- 21. 考慮点 (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); この場合 Seasar2 のコンテナ
} から request を得ている
}
return pm;
}
}
- 22. 考慮点 (PersistenceManager)
PMManager から入手する例
// ...
Season s = question.getSeason();
s.setCnt(s.getCnt()+1);
PMManager.getPm().makePersistent(s);
public Season getSeason() {
if (this.seasonKey == null) {
return null;
}
return PMManager.getPm()
. getObjectById(Season.class, this.seasonKey);
}
- 23. 考慮点 (Index)
検索条件によっては明示的に Index を定義す
る必要がある。
Google の資料で Index 定義が必要な検索とは
複数のソートが指定された場合
Key の逆順ソートの場合
== でない条件と == の条件がある場合
== の条件と ancestor フィルターがある場合
Local 環境で OK でも Deploy した環境で NG の場
合がある
- 24. 考慮点 (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>
- 25. その他の考慮点
kind 間の Join による読み込みはできない
group by を使用した集約関数は使用できない
count(*) / sum(xxx) 等
検索条件に != 、 IN は使用できない
Python ではクライアント側の実装として実現し
ている
検索条件の組み合わせで or 、 not は使用で
きない