Slim3 Source Code Reading No9 ModelRef
Slim3 Source Code Reading #9 - ChugokuGTUG
に参加してきました!
今回も@ttyokoyama 殿と2人だったので、まったりと進みました。
まぁ、他の方が参加していても、まったり進むのですがw
T.Yokoyamaのブログ: Slim3 Source Code Reading #9
今回読んだのはModelRefです。
ModelRefは1対多などのリレーションシップを表現するための機能です。
リレーションシップ - Slim3 日本語サイト(非公式)
この機能を使えば、リレーションシップをある程度タイプセーフに作成できます。
例えば、ModelRef#setModel()に誤ったModelを渡せばコンパイル時にエラーとなります。
また、ModelRef#setKey()でも、実行時にエラーとなります。
取得も簡単な記述でできます。
まず、1の側のModelを取得するgetModel()です。
ModelRef#getModel()
一度取得すると、メンバに保持するようになっています。
public M getModel() {
if (model != null) {
return model;
}
return refresh();
}public M refresh() {
if (key == null) {
return null;
}
model = Datastore.getWithoutTx(getModelMeta(), key);
return model;
}
また、ここでDatastoreへのアクセスを行なっています。
次は、双方向1対1の場合などに使う、getModel()です。
InverseModelRef#getModel()
こちらも一度取得すると、メンバに保持しています。
public M getModel() {
if (model != null) {
return model;
}
return refresh();
}public M refresh() {
Key key = getOwnerKey();
if (key == null) {
return null;
}
model =
Datastore.query(getModelMeta()).filter(
mappedPropertyName,
FilterOperator.EQUAL,
key).asSingle();
return model;
}
また、Datastoreへのアクセスも行なっていますが、こちらは何故かQueryです。
Queryである理由を考えましたが、特に何も思いつかず・・・。
最後は、多の側のgetModelList()です。
InverseModelListRef.getModelList()
こちらはQueryでKeyと一致するものを取得しています。
public ListgetModelList() {
Key key = getOwnerKey();
if (key == null) {
modelList = new ArrayList();
} else {
query.filter(mappedPropertyName, FilterOperator.EQUAL, key);
if (!sortsSet) {
query.sort(defaultSorts);
}
modelList = query.asList();
}
return modelList;
}
この他にも通常のQueryと同じようにfilterやsortもできます。
また、filterInMemoryとsortInMemoryもありますし、カーソルを扱うasQueryResultListもあります。
ただし、ancestor query を扱うことはできません。
まぁ、RDB的なリレーションシップなので、KVSの考え方であるancestorと混ぜて使うような複雑なことにはならない気がするので、問題ないと言えば問題ないですね。
ただ、ここまで書いておいてあれなのですが、僕はModelRef使ってないです。
理由は大きく分けると以下の3つです。
- ReadOpsの節約策が取りにくい。
- 意図しない箇所で、Datastoreへのアクセスが発生する可能性がある。
- あまり、JOINしたいと思うことがない。
ReadOpsの節約策が取りにくい
Datastoreへの無駄なアクセスを減らすのは、課金的にもパフォーマンス的にも良いことです。
そのための策は色々とありますが、どれもModelRefを使うとやりにくくなってしまいます。
Memcacheに入れるという手段もModelRefはサポートしてくれないため、自分でやる必要があります。
また、元のModelを取得しないとそれに関連するModelを取得することができません。
そんなことをしなくても、元のModelのKeyを生成して、queryを投げるとか、queryを避けるために元のModelにList
意図しない箇所で、Datastoreへのアクセスが発生する可能性がある
まぁ、これは僕の趣味なのですが・・・。
あまり、Service以外でDatastoreにアクセスしたくないのですね。
ModelRefを使うとController側に返ってきた後に、getModel()とかするとDatastoreへのアクセスが発生していまいます。
また、queryでfilterやsortを使うと、カスタムインデックスが必要になってしまう可能性があります。
その辺りが制御しづらくなるので、あまり使いたくありません。
あまり、JOINしたいと思うことがない
なかなか、本末転倒なのですが・・・。
GAEのDatastoreにおいて正規化はしばしば悪となるため、そんなに正規化していません。
検索できなくなるし、ReadOpsも増えてしまいます。
そのため、あまりRDBのJOINみたいなことをしたいと思うことがありません。
ということで、僕はModelRef使いません!ってのが結論です!
なんで、読んだし!って感じですが、実は以前使うかどうか悩んだ時に、既に読んじゃってたんですよねー。
それと合わせてGAE Office HourでModelの設計なんかを聞いて、使わない方が無難であるという結論に達してしまったわけです。
testerの方も読み進めていましたが、長くなってきたので、エントリーを分けます!