PoEAA Part 1 Chapter 3 Mapping to Relational Databases
- 前章の話
- Mapping to Relational Databases
- Architectural Patterns
- The Behavioral Problem
- Reading in Data
- Structural Mapping Patterns
- Building the Mapping
- Using Metadata
- Database Connections
- Some Miscellaneous Points
- Further Reading
- 英語
前章の話
Mapping to Relational Databases
- データソース層の役目: さまざなまインフラとのコミュニケーション
- 特にDB
- 特にRDB
Architectural Patterns
- ドメインロジックからDBへの問い合わせかたは設計に左右されるという話
- SQLでのDBアクセスとドメインロジックは分離しろ、という話
- Gateway: SQLでのデータアクセスの責務
- Gatewayの分類
- 著者(Martin Fowler)は、シンプルなアプリケーションでもgatewayをつかう
- Table Data GatewayをTable Moduleパターンと併用するのはすぐに思いつくこと
- Table Data Gatewayはストアドプロシージャとも相性がいい
- ストアドプロシージャの集まりがTable Data Gatewayクラスであるとみなせる
- Domain Modelでドメイン層を設計する場合は、いずれのGatewayもイマイチ
- 間接参照しすぎorしなさすぎ
- 単純なアプリケーションでは、Domain Modelは複雑な構造にはならず、DBの構造とよく一致する
- ビジネスロジックが複雑になっていくと、Active Recordは破綻する
- Domain ModelとRDBのスキーマとを完全に分断したくなる
- Domain Modelを採用するなら、Gatewayで始めるのはオススメしない
- 「テーブル」という言葉について
- ビュー、クエリ、ストアドプロシージャに読み替えてもいい
- ビューの更新でデータの一貫性が失われる問題
- 同一の実表を参照している2つのビューに対して更新を行なった結果、二重更新になってしまう
- 混乱するからやめようね
- OOデータベースの話
- OOデータベースを使えないなら、O/Rマッパーの購入を検討したほうがいい
- 本書でもData Mapperの実装方法についてあれこれ提示しているが、とても骨が折れる
- 自前で作るより、何年もかけて鍛え上げられた市販のものを使ったほうがいい
- 自前は、作るコストもメンテのコストもかかる
- JDO in Javaとかある
- 市販のO/Rマッパーを利用するにせよ、その設計について理解することはよいこと
- マッピングのオプションがたくさんあり、その選定に有用
- O/Rマッパーの導入は大きな前進だが、全部良しなにやってもらえるわけではない
The Behavioral Problem
- O/Rマッパーにより、オブジェクトとテーブルのデータ構造のマッピングがなされる
- 振る舞いがもう一つの柱
- データ永続化の振る舞いをどうするか
- レコード自身が保存・読み出しする: Active Record
- 多数のレコードを永続化するとなると、一貫性の問題が浮上する
- あるレコードの主キーを、別のレコードの外部キーが参照するような場合とか
- 複数のオブシェクト読み出し中に、別プロセスでオブジェクトが書き換えられてはいけない
- こういった同時性に関する問題は5章にて
- 一貫性に関してはUnit of Workで解決
- 一度に変更を加える
- レコードのsave()メソッドを明示的に呼び出すのではなく、Unit of Workのcommit()を実行する
- 同じレコードを2回読み出して、オブジェクトのコピーを生み出してはいけない
- 別々にupdateすると混乱を招く
- 1回目の変更が上書きされてしまう
- Identity Mapで一意性を担保せよ
- キャッシュみたいな感じ
- ただし、パフォーマンス目的ではない。一貫性目的
- キャッシュみたいな感じ
- 別々にupdateすると混乱を招く
- Domain Modelを採用すると、1つのDomain Modelに複数のオブジェクトが紐づくことがある
- 注文オブジェクトには顧客オブジェクトと商品オブジェクトが紐づく
- 毎回必ずcustomersとproductsテーブルからもデータを取得するのは非効率
- 空間
- 時間
- Lazy Load: 必要になったら読み込む
- 【補】GoFでいうVirtual Proxyですね
Reading in Data
- finderメソッド
find(id)
findFOrCustomer(customer)
- どこに置く
- テーブルオブジェクト
- insertとかupdateとかと併用できる
- 行オブジェクト
- insertとかupdateとかと併用できない
- insertやupdateはテーブルに対して行うものだから
- insertとかupdateとかと併用できない
- テーブルオブジェクト
- 行オブジェクトのクラスメソッドにしたら?
- 行オブジェクトにしたいときは、finderオブジェクトを作ると良い
- finderメソッドやfinderオブジェクトのメソッドは、DBに対して行うもの
- オブジェクトに対して行うものではない
- オブジェクト100個に対応するデータを問い合わせたい場合、クエリを100回投げるのではなく、1回で取ってくるべき
- データ取得においては、足りないよりも多いほうがマシ
- 50件でいいのに200件取れちゃうクエリのほうが、50回クエリを投げるより良い
- 複数テーブルに複数クエリを投げるのではなく、JOINして1クエリで取得する
- パフォーマンスについては、推測するな計測せよ
Structural Mapping Patterns
- O/Rマッピングというと、メモリ上のオブジェクトとDBの行との対応の意味で使われることが多い
- Row Data Gateway
- Active Record
- Data Mapper
- テーブルに対応するTable Data Gatewayは関係ないことが多い
Mapping Relationships
- オブジェクトとRDBの関連とでは、リンクの取り回しが異なるよねという話
- has-one, one-to-many関係
- O: 所持側が所持される側の参照を保持する
- R: 所持される側が所持する側を参照する(外部キー)
- コレクションを直接扱えないため
- Relationalにおいて、one-to-many関係を扱うと、many側がone側を参照する
- 一意性問題が浮上する
- many-to-manyの関連の場合、連関テーブルが必要になる
- Association Table Mapping
- コレクションの順序の話
- 参照の完全性の話
- 環境が完全性チェックしてくれるなら使わない手はない
- 環境の支援が受けられなければ、自分でチェックすることになる
- 正しい順番でupdateされるか
- Value Object
- 単体でテーブルの行になるほど大きくないオブジェクト
- date range(始点・終点、年月日)
- money(金額・単位)
- 他のオブジェクトにぶら下がる(Embedded Value)
- 独自のセマンティクスがあり、パースのロジックなどがある
- そこで一意性を担保すればいいので、Integrity Map不要
- 単体でテーブルの行になるほど大きくないオブジェクト
- 巨大で複雑なコンポジットをRDBで扱うには
Inheritance
Single Table Inheritance | Concrete Table Inheritance | Class Table Inheritance | |
---|---|---|---|
概要 | クラスツリー全体に対して1つのテーブル、和集合的なスキーマ | 各具象クラスについて1つのテーブル、祖先との和集合的なスキーマ | 各インタフェース、抽象クラス、具象クラスに対して1つのテーブル、1対1対応のスキーマ |
メリット | 変更が楽、JOIN不要 | JOIN不要 | インタフェース・クラスとテーブルとの対応関係が単純 |
デメリット | 空カラムが無駄 | 変更の影響大。祖先インタフェース/クラスの変更が全テーブルに波及 キーが複数テーブルに分散するため、一貫性の確保が難しい |
1つのオブジェクトのデータを取得するのに複数のテーブルのJOINが必要 |
- 排他的なものではない
- 混用すると複雑になるけどね
- どれが一番良い、というものではない
- 著者は初めにSingle Table Inheritanceを選ぶ傾向
- 扱いやすい
- リファクタリングの余地がある
- 著者は初めにSingle Table Inheritanceを選ぶ傾向
- interfaceの多重実装にも対応可能
Building the Mapping
- O/Rマッピングするにあたって考えられるシチュエーション
- スキーマを好きに選べて、ドメインロジックがさほど複雑でないなら:
- ドメイン層にDomain Modelを使うなら:
- データソース層
- Domain ModelとDBとを完全に分離したいなら:
- Data Mapper
- Domain Modelとテーブルとが同型であることが理にかなっているなら
- Active Record
- Domain ModelとDBとを完全に分離したいなら:
- データソース層
- モデルを最初に構築してうまくいくのは、開発のイテレーションのサイクルが短いときだけなので注意
- スキーマがすでにあるなら:
Double Mapping
Using Metadata
- 定型コードにまみれるので、メタデータを設けて汎化しましょうという話
<field name="customer" targetClass="Customer" dbColumn="custID" targetTable="custoemrs" lowerBound="1" upperBound="1" setter="loadCustomer />
Database Connections
- connection
- アプリケーションとDBとのインターフェースを務める
- 【補】PHPのPDOとかのことかと
- connectionの確立の切断は高コスト
- なので、プールして使いまわしたい
- 執筆時点で、大抵の環境でサポートされており、自前で書く必要はない
- もしも自前で書くことになったら、そもそも必要かどうかから検証せよ
- connectionの確立・切断は高速化してきている
- 必要ないならプールしないに越したことはない
- 環境によりconnectionのプールがサポートされている場合、connectionを生成しているのかプールから取ってきているのか意識する必要はない
- 実際に何が起きているかによらずopen, closeと表現することにする
- コストの高低によらず、connectionの管理はあくまでプログラマの責任
- 使うときにopen
- 使い終わったら忘れずにclose なる早で
- connectionの管理方法
- 不変なデータの読み出し等、トランザクションを使わない場合でも、connectionのプールは有効
- 一瞬しか使わないconnectionをいちいち確立・切断しない
- disconnected Record Set
- 利用時の一連の流れ
- connection openしてDBからデータ読み出し
- connection closeしてフィールド操作
- トランザクション開始・connection openしてDBへ書き込み
- 2.の最中にDBが書き変わりうることを考慮すること(同時性問題)
- 本書の後の方で扱う
- 利用時の一連の流れ
- 環境依存の部分多し
Some Miscellaneous Points
select *
の問題- 新しい列を追加したら壊れる
- 列番号を使用している場合、列順を追加したら壊れる
- 単純なCRUDのテストが有効
- プリコンパイルSQLは性能面で有効
- prepared statementのこと
- SQLクエリを組み立てるのに文字列結合使うな
- コネクションをDBオブジェクトとして汎化し、置換可能にする
Further Reading
- Brown and Whitenack
- Ambler
- Yoder
- Keller and Coldwey
英語
- comprise
- Consist of; be made up of.
- far-reaching
- Having important and widely applicable effects or implications.
- get at
- Reach or gain access to (something)
- feisty
- Touchy and aggressive.
- anecdotal
- not necessarily true or reliable, because based on personal accounts rather than facts or research.
- 要出典
- make a dent
- To make noticeable progress in a task or to consume a noticeable amount of something of which there is a large quantity.
- get the hang of something
- to learn how to do something, esp. when it is not simple or obvious:
- コツを掴む
- gotcha
- A sudden unforeseen problem.
- resilient
- (of a substance or object) able to recoil or spring back into shape after bending, stretching, or being compressed.
- on the whole
- Taking everything into account; in general.