JPAのサンプルは、たいていデータ構造が手抜き

O/Rマッパーに対してネガティブな意見を見つけた。

O/Rマッパーの話 - 工夫と趣向と分別と。
http://d.akinori.org/?date=20060926#p01

一般に、テーブル設計をクラス設計に擦り寄せれば、リレーションを駆使する必要は減る。この罠にはまると、
オブジェクトクラスにそのまま対応させたテーブルを作ってしまい、コードの見かけはきれいでもデータは
ぐだぐだな代物ができがちだ。

オブジェクトグラフの構造をそのままデータ構造にしたらグダグダになりますよ、ということだな。
これ読んで思い出すのが、ちょっと前にJPAの正しい使い方を知ろうと思ってサンプルを漁ってみた時のこと。
実戦で使えるデータ構造を作っているサンプルが無くて、結局JPAてどうやって使えばいいのか分からなかった。
焦点は event の扱いで、サンプルは以下の3パターンに分かれる。

1. そもそも event がない

古い入門記事なんかに、このパターンが多い。
これだけ読んでも event をどうやって表現すればよいのか分からない。

2. マスタを更新すると過去の事実が変わってしまう

一番多いのがこの形。例えば受注まわりのオブジェクトを、そのままテーブルにマップしている形。

+----------+       +----------+      +----------+      +----------+   
|   顧客   |       |   注文   |      | 注文明細 |      |   商品   |   
+----------+-----●+----------+----●+----------+●----+----------+
|          |       |          |      |          |      |   単価   |   
+----------+       +----------+      +----------+      +----------+   

ここでは注文明細クラスが商品クラスをManyToOneで持っている。
単価は商品クラスのプロパティで、かつ商品クラスは商品マスタテーブルにマップされるので、マスタの単価を変更すると過去の売上が変わってしまう。
当然、このままでは実戦には使えない。本番ではどうモデリングすればいいのか知りたくなる。

3. オブジェクトモデルに見ちゃいけないデータが混入している

IBMがここ↓
http://www-06.ibm.com/jp/developerworks/java/060412/j_j-ejb3jpa.shtml
で公開しているサンプルは、受注明細に単価をコピーしているので、単価を変更しても過去の売上が変わったりしない。

+----------+       +----------+      +----------+      +----------+   
|   顧客   |       |   注文   |      | 注文明細 |      |   商品   |   
+----------+-----●+----------+----●+----------+●----+----------+
|          |       |          |      | 販売単価 |      |   単価   |   
+----------+       +----------+      +----------+      +----------+   

一方で、受注のオブジェクトグラフには単価が2箇所にあることになる。
受注データを参照する機能を作るプログラマは、商品オブジェクトが持っている単価を参照しないように注意しなくてはならない。それは販売単価じゃなくて、現在の単価だから。
仮にマスタの単価を参照して売上合計を算出するプログラムを書いても、このバグはなかなか見つからないだろう。


また、データが複数のオブジェクトにまたがって存在すると、対応する手続きの置き場所も1箇所に決められなくなって、クラスの設計がゆがむ。
例えば「商品マスタ表示画面」「売上明細表示画面」の2画面で、販売単価から本体価格・消費税額を導出するメソッドが必要になったとして、上記サンプルでは、そのメソッドを商品クラスと注文明細クラスの両方に実装しなくてはならない ... のだろうか*1

またおんなじことを言ってますが

言いたいことはいっつもおんなじでアレなんですが。
クラスの設計は、データと手続きの2軸で考えているうちはいいのだが、「RDBへの永続化」という3軸目を加えると急に難しくなる。
「Entityクラスの属性を、データベースのただ一つカラムに対応づける」という考え方が窮屈になる。
Entityクラスのある属性に対応するカラムは、ユースケースによって異なる、というのが本当にやりたいことなのだが、O/Rマッパーがそれを許さない。
O/Rマッパーを使うなら、オブジェクトグラフを歪めるか、データ構造を歪めるかを選択しなくてはならない。だから、O/Rマッパーは意外と便利じゃない。

*1:私はデータと手続きが分離していても平気なので、ユーティリティクラスに消費税額算出staticメソッドを作って逃げると思うが、オブジェクト指向派の人ならどうするだろうか