9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ちょうぜつAdvent Calendar 2022

Day 9

IdentityField & ForeignKeyMapping & AssociationTableMapping

Last updated at Posted at 2022-12-08

09.IdentityField & ForeignKeyMapping & AssociationTableMapping.png

ここからは「オブジェクトリレーショナル構造パターン」に分類されるパターンです。プログラミング言語のオブジェクト概念と、リレーショナル・データベースの行の違いを、どう対応付けて構造を同期するかが主題です。

Identity Field は、クラス定義にテーブルの主キー値を格納するフィールド (プロパティ) を設けて、オブジェクトと行を対応させる手法です。これはおそらく、他のパターンと異なり、選択の余地があるパターンではありません。

オブジェクトを扱うプログラミング言語は、そのランタイム内で、メモリアドレスや内部的な識別子を用い、変数によって暗黙的にオブジェクトを一意特定します。なので本来であれば、ユーザー領域に ID のようなものを設ける意味はありません。しかし、データベースは多くの場合、明示的な主キー値によって行を特定します。主キー (あるいはユニーク制約を持つカラム) のないテーブルは、値は入っているけれど、データを確実に「それ」と特定する手段を持ちません。

有意な値を行特定の代表とするナチュラルキーを付けられない場合、ORM ではサロゲート (代理) キーが好まれます。サロゲートキーは重複のないランダムな値 (整数シーケンスなど) です。これをプログラム言語の内部識別子のようなものと考えるのです。サロゲートキーは単なる対応付けのためのものなので、そこに意味を求めるべきではありません。サロゲートキーを Identity Field としたものを使って業務上のコミュニケーションを取っていると、データベースシステムを移行したとき、サロゲートキーが付け変わるかもしれません。

Foreign Key Mapping は、リレーショナルデータの結合 (JOIN) に使われる外部キーを利用して、テーブルの関係をオブジェクトの一対多関係に対応付けるパターンです。

本から見た著者は 1 人です (共著でない場合)。本は著者に「属している」ことになります。リレーショナルモデルでは、書名などの属性と、著者をユニーク特定する情報 (出版社が決めた著者 ID など) が強く関連付いています (この値同士の結びつきが「リレーション」です)。そのような本テーブルと、著者 ID を主キーとした著者テーブルを結合させると、書名と著者名のリストを作ることができるのが SQL です。データベースの外部キー制約は、必ずこの結合ができることを保証するために、本テーブルの著者 ID に、著者テーブルの主キーに実在しない値が入るのを防いでくれます。

いっぽう、プログラミング言語のデータ構造は、Book オブジェクトが author 属性で著者オブジェクトを参照しているような形をしています。そこで、外部キーを利用して、テーブルの関係をオブジェクトの参照関係にマッピングします (このテーブル間の関係は、リレーションではなく「リレーションシップ」と呼ばれます)。

複数の本オブジェクトがひとつの著者オブジェクトに属すということは。裏を返せば、著者オブジェクトは複数の本オブジェクトを自身の著書として「所有している」ことになります。Author クラスの books 配列で、複数の本を参照している様子を表現できます。外部キーはこの関係にも利用できます。このとき注意しなければならないことは、著者テーブルのカラムには books 配列がない (というより、リレーショナルモデルにはそもそも配列という値型がない) ということです。オブジェクトで所有関係をモデリングしたい場合、外部キーカラムを持つテーブルは目の前のクラスに対応するテーブルではなく、「所有される側」のテーブルになります。

Association Table Mapping は、Qiita の記事とカテゴリタグの関係のような構造をマッピングする方法です。記事はタグを複数持ちますが、そのタグは特定の記事に固有なものではなく共有です。複数の記事が共有しているタグもまた、そのカテゴリに属す記事を複数持ちます。このような関係は多対多です。

こうした (ちょっと面倒な) 多対多をリレーショナルモデルで扱うには、記事テーブルとタグテーブルをいったん独立したものとして完結させます。そして、記事の主キーとタグの主キーの 2 カラムだけを持ち、それらをともに外部キーとするテーブルを設けます。このテーブルに、どの記事とどのタグの間に関係線が引けるかという情報を入れていきます。2 つのカラムの値セットには、ユニーク制約を付けることができます。通常、記事は重複して同じタグを持たないし、タグは重複して同じ記事を持ちません。

これをオブジェクトにマッピングするには、Article クラスに tags 配列を持たせ、Tag クラスに articles 配列を持たせておくことになります。もちろん参照方向は片方だけでもかまいません。ただし、片方だけにした場合にプロパティだけを見て、Foreign Key Mapping の「所有」だと勘違いしないように気を付けましょう。ポイントは、配列の要素が専有 (コンポジション) なのか共有 (アグリゲーション) なのかです。

Association Table Mapping の割当用中間テーブルは、原則、外部キー以外の情報を持ちません。純粋な多対多関係は、本質的にはコレクションが Set (順序を問わず要素がユニークなコレクション) になります。割り当てには順序情報がないので、実際にマッピングするときは、要素の何らかの属性をソート基準として利用します。

もし多対多関係に恣意的な順序制御などを付けたいと思ったなら、それは二重の Foreign Key Mapping になってきます。記事とタグを結びつけるテーブルに、サロゲートキーの主キーと、追加情報カラムを追加します。それを「記事タグエントリー」といったような名前のクラスにマッピングし、Book が持つのは tagEntry 配列、Tag が持つのは articleEntry 配列という形にします。

articel.tagEntry[0].tag;
tag.articleEntry[0].articel;

記事に付いたタグの順序を入れ替えるプログラムはこのように表せるでしょう。

a = articel.tagEntry[0].tagSeqNo;
b = articel.tagEntry[1].tagSeqNo;
articel.tagEntry[0].tagSeqNo = b;
articel.tagEntry[1].tagSeqNo = a;

Association Table Mapping と多段 Foreign Key Mapping が表す情報構造は、本質的に同じです。どちらが適しているかは、割り当て情報に、割り当て以外の情報がいっさい求められないと言い切れるかどうかで見極めていけます。

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?