GORMでの継承とデータベーススキーマ
GORMでの継承によってデータベース上でどのようにスキーマ定義するかというお話です。
Grailsのデフォルトの継承戦略はtable-per-hierarchyになっています。例えば次のようなドメインがあったとします。
class Parent { String name }
class Sub1 extends Parent { String sub1Value }
class Sub2 extends Parent { String sub2Value }
というクラス構成でschema-exportすると以下のようになります。
create table parent ( id bigint generated by default as identity, version bigint not null, name varchar(255) not null, class varchar(255) not null, sub2value varchar(255), sub1value varchar(255), primary key (id) );
データベース上ではすべて同一のテーブルにプロパティを突っ込む形になって、同一テーブルに突っ込んでる手前サブクラスのプロパティにNotNullを設定できなくなるのがデメリットです。またclassというカラムがデータベース上に作成されて、この情報を元にどちらのドメインの情報かを区別しています。
こんどはtablePerHierarchyをfalseにしてみます。
class Parent { String name static mapping = { tablePerHierarchy false } }
こんどは以下のようなスキーマになります。
create table parent ( id bigint generated by default as identity, version bigint not null, name varchar(255) not null, primary key (id) ); create table sub1 ( id bigint not null, sub1value varchar(255) not null, primary key (id) ); create table sub2 ( id bigint not null, sub2value varchar(255) not null, primary key (id) );
今度はドメインクラスに1対1で対応付く形でテーブルが定義されます。先ほどとの違いは、NotNullが設定できるが、検索時にJOINするためパフォーマンスに難点があるといったとこでしょうか。
最後にもう一つParentをドメインディレクトリから普通のsrcディレクトリに移動し、abstractとする方法です。
abstract class Parent { String name }
この場合は以下のようになります。
create table sub1 ( id bigint generated by default as identity, version bigint not null, name varchar(255) not null, sub1value varchar(255) not null, primary key (id) ); create table sub2 ( id bigint generated by default as identity, version bigint not null, name varchar(255) not null, sub2value varchar(255) not null, primary key (id) );
完全に別テーブになって、親テーブルを元に検索出来ない(Parent.findBy...)、DRYじゃないけど、互いに疎である必要があるは必然的にこれになります。