インピーダンス・ミスマッチを解決する、O/Rマッピングの設計

作成:2006年8月1日

吉田誠一のホームページ   >   ソフトウェア工学   >   技術コラム   >   オブジェクト指向

オブジェクト指向言語であるJava言語からリレーショナル・データベースにアクセスする際には、インピーダンス・ミスマッチという問題が起こることは、よく知られている。

とはいえ、Java言語でデータベースにアクセスするソフトウェアを作るのは、難しいことではない。インピーダンス・ミスマッチは、コーディングが難しかったり、ソフトウェアを動かすと障害が起きる、といった類の問題ではないため、結局、何が問題なのか、良く分からないかもしれない。

ここでは、オブジェクト指向言語からリレーショナル・データベースにアクセスする際に、具体的にどのような問題に遭遇するのか、分かりやすい例を使って紹介しよう。

さらに、インピーダンス・ミスマッチを解決すべく、いくつかのO/Rマッピング方法を考えてみて、その良し悪しを検討してみよう。

ここで紹介する「伝言板システム」では、シャドウ・インフォメーションをインターフェースで隠蔽する方法を採用すると、インピーダンス・ミスマッチをうまく解決できた。

目次

  1. 例題「伝言板システム」
  2. データ・モデル
    1. データベースのデータ・モデル
    2. ビジネス・ロジックのデータ・モデル
  3. デザインパターン
    1. DAOパターン
    2. トランスファー・オブジェクト
  4. インターフェースの設計
    1. ボトム・アップの設計
    2. トップ・ダウンの設計
  5. インターフェースの歪み
    1. インピーダンス・ミスマッチ
    2. シャドウ・インフォメーション
  6. O/Rマッピング
    1. 2つのデータ・モデルの特徴を兼ね備えたトランスファー・オブジェクトを使う
    2. シャドウ・インフォメーションを明示的に使う
    3. シャドウ・インフォメーションをインターフェースで隠蔽する

例題「伝言板システム」

ここでは例として、好きな時に好きなメッセージを残しておける、伝言板システムを作ることにしよう。

伝言板

この伝言板システムは、オブジェクト指向言語であるJava言語で作ることにしよう。データベースは、リレーショナル・データベースを使う。

データ・モデル

データベースのデータ・モデル

リレーショナル・データベースを使うため、データベースのデータ・モデルは、リレーショナル・モデルとなる。

ここでは、次のER図(Entity-Relationship Diagram)のようなモデルになる。

ER図

冒頭の伝言板の例では、次のようなテーブルができる。

テーブル

ビジネス・ロジックのデータ・モデル

オブジェクト指向言語であるJava言語で作るため、ビジネス・ロジックのデータ・モデルは、オブジェクト・モデルとなる。

ここでは、次のUML(クラス図)のようなモデルになる。

クラス図

冒頭の伝言板の例では、次のようなオブジェクトができる。

オブジェクト図

デザインパターン

DAOパターン

データベースを使うシステムでは、一般に、データベースのテーブル定義とビジネス・ロジックとを切り離すために、DAOパターン(Data Access Objectパターン)を採用する。

DAOパターンを使わないシステムは、次のようなものになる。ビジネス・ロジックの中に直接SQL文が書かれるため、データベースのテーブル定義が変化すると、ビジネス・ロジックも修正しなくてはならない。

DAOパターンを使わないシステムのレイヤー構成

DAOパターンでは、SQL文の生成をDAOに閉じ、ビジネス・ロジック層に対しては、データベースのテーブル定義を隠蔽する。

DAOパターンのレイヤー構成

トランスファー・オブジェクト

DAOパターンでは、DAOとビジネス・ロジック層の間でのデータの受け渡しに、トランスファー・オブジェクトを使う。

トランスファー・オブジェクトは、バリュー・オブジェクトとも言い、自分自身では永続化の処理を持たない、単純なデータである。O/Rマッピング・フレームワーク「Hibernate」では、エンティティ・クラスと呼ばれるものが、これに該当する。

インターフェースの設計

DAOパターンを使うシステムでは、以下の2点について考える必要がある。

  • DAOのインターフェース
  • トランスファー・オブジェクトが持つデータ

伝言板システムでは、DAOのインターフェースとして、下記の3つのメソッドを用意することにしよう。

  • 伝言を1つ追加する。(add伝言)
  • 伝言を1つ更新する。(update伝言)
  • すべての伝言を取得する。(getAll伝言)

これらのメソッドで受け渡すトランスファー・オブジェクトを、2つのアプローチで考えてみよう。

ボトム・アップの設計

データベースのモデル(リレーショナル・モデル)をベースに設計すると、次のようなトランスファー・オブジェクトができる。

このアプローチでは、トランスファー・オブジェクトに、リレーショナル・データベースにおけるキーを持たせる点が特徴である。

ボトム・アップで設計したトランスファー・オブジェクト

このトランスファー・オブジェクトを使って、DAOのインターフェースを次のように定める。

ボトム・アップで設計したDAO

しかし、この設計には、次のような問題がある。

  • 新しい伝言を追加する際にも、伝言番号を受け渡している。
    • 実際には、伝言番号はデータベースで自動的にユニークな値が付けられる。
  • 伝言を1つ更新する際には、伝言番号だけは書き換えてはいけない。
    • もし伝言番号を書き換えてしまうと、取得した伝言とは別の伝言を勝手に書き換えてしまう。

受け渡しに使うトランスファー・オブジェクトのデータの中に、指定すべきものと指定しなくて良いもの、変更して良いものと変更してはいけないものが混在してしまうため、分かりにくい。どれを書き換えて良いのかは、暗黙の了解になってしまう。

また、次のような問題もある。

  • すべての伝言を取得しても、利用者の名前を表示できない。
    • すべての伝言だけではなく、すべての利用者も取得する必要がある。

トップ・ダウンの設計

ビジネス・ロジックのモデル(オブジェクト・モデル)をベースに設計すると、次のようなトランスファー・オブジェクトができる。

このアプローチでは、トランスファー・オブジェクトどうしが、直接、参照し合う点が特徴である。

トップ・ダウンで設計したトランスファー・オブジェクト

このトランスファー・オブジェクトを使って、DAOのインターフェースを次のように定める。

トップ・ダウンで設計したDAO

しかし、この設計には、次のような問題がある。

  • 伝言を1つ更新する際に、リレーショナル・データベースにおけるキーが渡されない。
    • データベースに記録されている伝言のうち、どの伝言を更新するのか分からない。
  • 伝言を追加または更新する際に、利用者の名前しか渡されない。
    • 利用者IDが分からないため、データベースに記録できない。

受け渡されるオブジェクトと、データベースのレコードとの対応関係が失われているため、データベースの更新ができない。

インターフェースの歪み

インピーダンス・ミスマッチ

DAOは、データベースのモデル(リレーショナル・モデル)と、ビジネス・ロジックのモデル(オブジェクト・モデル)との間で、データの受け渡しを仲介する存在である。

だが、そこで受け渡すトランスファー・オブジェクトは、リレーショナル・モデルをベースに設計しても、オブジェクト・モデルをベースに設計しても、どちらのアプローチでも問題が生じる。

このように、リレーショナル・モデルと、オブジェクト・モデルは、うまくかみ合わない。これを、インピーダンス・ミスマッチと呼ぶ。

シャドウ・インフォメーション

この例では、リレーショナル・データベースにおけるキーの扱い方で問題が生じた。

オブジェクト・モデルには、キーという概念は登場しない。しかし、データベースにアクセスするためには、キーは必須である。

このように、設計では現れないのに、実装時には必要になってしまう情報を、シャドウ・インフォメーションと呼ぶ。

シャドウ・インフォメーションは、そもそもモデルに登場しないため、オブジェクト・モデルからはうまく扱えない。トランスファー・オブジェクトが持つデータのうち、キーだけは、指定しなくても良い、変更してはいけない、といった暗黙の了解が生じてしまったのは、そのためだ。

この他に、クラスの継承を使ったオブジェクト・モデルをデータベースに保存する場合にも、インピーダンス・ミスマッチが起こり、シャドウ・インフォメーションが必要になる。

O/Rマッピング

データベースのモデル(リレーショナル・モデル)と、ビジネス・ロジックのモデル(オブジェクト・モデル)という、異なる2つのデータ・モデルを結びつけることを、O/Rマッピングと呼ぶ。

トランスファー・オブジェクトと、DAOのインターフェースは、O/Rマッピングの方法によって、異なったものとなる。

2つのデータ・モデルの特徴を兼ね備えたトランスファー・オブジェクトを使う

一般的には、下記に挙げた、トップ・ダウンの設計と、ボトム・アップの設計の、双方の特徴を兼ね備えたトランスファー・オブジェクトを使うことが多い。

  • トランスファー・オブジェクトに、リレーショナル・データベースにおけるキーを持たせる。
  • トランスファー・オブジェクトどうしが、直接、参照し合う。

この方法では、次のようなトランスファー・オブジェクトを作る。

2つのデータ・モデルの特徴を兼ね備えたトランスファー・オブジェクト

このトランスファー・オブジェクトを使って、DAOのインターフェースを次のように定める。

2つのデータ・モデルの特徴を兼ね備えたトランスファー・オブジェクトを使う場合のDAO

O/Rマッピング・フレームワーク「Hibernate」でエンティティ・クラスを自動生成する場合も、この方法になる。

しかし、この方法では、ビジネス・ロジックではシャドー・インフォメーションを使用しない、という、暗黙の了解が必要になってしまう。

シャドウ・インフォメーションを明示的に使う

シャドウ・インフォメーションに伴う暗黙の了解を無くすために、リレーショナル・データベースにおけるキーを、ビジネス・ロジックでも明示的に扱う、という方法も考えられる。

この方法では、次のようなトランスファー・オブジェクトを作る。

新しい伝言を追加する際に指定すべきデータ、伝言を更新する際に変更しても良いデータだけが、1つのオブジェクトになっている。

シャドウ・インフォメーションを明示的に使う場合のトランスファー・オブジェクト

このトランスファー・オブジェクトを使って、DAOのインターフェースを次のように定める。

この方法では、DAOのインターフェースでも、リレーショナル・データベースのキーを積極的に使用する。

シャドウ・インフォメーションを明示的に使う場合のDAO

このようにすると、暗黙の了解が無い、明確なインターフェースが定義できる。

但し、この方法では、データベースのモデル(リレーショナル・モデル)を、そのままビジネス・ロジックに公開している。

ビジネス・ロジック層のデータ・モデルをオブジェクト・モデルとし、DAOの役割を、単にデータベースのテーブル定義を隠蔽するだけではなく、パーシステンス層との境界でモデルの変換を行うもの、と考えるのであれば、この方法は望ましくない。

シャドウ・インフォメーションをインターフェースで隠蔽する

シャドウ・インフォメーションに伴う暗黙の了解を無くすために、シャドウ・インフォメーションをインターフェースで隠蔽する、という方法も考えられる。

トランスファー・オブジェクトが持つデータのうち、ビジネス・ロジックで必要とされるデータのみを参照できるように、アクセスを制限したインターフェースを用意する。ビジネス・ロジックでは、トランスファー・オブジェクトそのものではなく、アクセスを制限したインターフェースを通じて操作する。

この方法では、トランスファー・オブジェクトの実体としては、前述した、一般的なトランスファー・オブジェクトを使う。

トランスファー・オブジェクトの実体は、リレーショナル・データベースにおけるキーを持つ。一方、ビジネス・ロジックが使うインターフェースでは、キーを取得することはできるが、キーの値を書き換えることはできない。つまり、キーをRead Onlyな属性としている。

シャドウ・インフォメーションをインターフェースで隠蔽する場合のトランスファー・オブジェクト

このトランスファー・オブジェクトを使って、DAOのインターフェースを次のように定める。

シャドウ・インフォメーションをインターフェースで隠蔽する場合のDAO

このようにすると、暗黙の了解が無い、明確なインターフェースが定義できる。

しかも、ビジネス・ロジックから見ると、トランスファー・オブジェクトやDAOのインターフェースは、リレーショナル・データベースにおけるキーが登場しない、オブジェクト・モデルになっている。

そのことを、データベースから取得した伝言の内容を書き換えて、データベースに保存し直すケースで確かめてみよう。

DAOでは、トランスファー・オブジェクトの実体である「伝言Implクラス」のインスタンスを生成して、そこに、リレーショナル・データベースに記録されているキーの値をセットしてから、ビジネス・ロジックに渡す。

ビジネス・ロジックでは、DAOから受け取った伝言のインスタンスの内容を書き換える。この際、ビジネス・ロジックは「伝言インターフェース」を通じてしか操作できないため、トランスファー・オブジェクトの実体が持つキーの値は変わらない。

ビジネス・ロジックは、内容を書き換えた伝言のインスタンスを、DAOに渡す。

DAOでは、ビジネス・ロジックから受け取った伝言のインスタンスが、「伝言Implクラス」のインスタンスかどうかは分からない。しかし、「伝言インターフェース」の「get伝言番号」メソッドによって、リレーショナル・データベースにおけるキーを得、該当するレコードの内容を更新することができる。

Copyright(C) Seiichi Yoshida ( comet@aerith.net ). All rights reserved.