永続クラスの定義
永続クラスは、永続オブジェクトを定義するクラスです。ここでは、このようなクラスの作成方法について説明します。
このページで示されているサンプルは、Samples-Data (https://github.com/intersystems/Samples-DataOpens in a new tab) のものです。(例えば) SAMPLES という名前の専用ネームスペースを作成し、そのネームスペースにサンプルをロードすることをお勧めします。一般的な手順は、"サンプルのダウンロード" を参照してください。
永続クラスの定義
永続オブジェクトを定義するクラスを定義するには、そのクラスの プライマリ (最初の) スーパークラスを %PersistentOpens in a new tab にするかその他の永続クラスであることを確認してください。
例 :
Class MyApp.MyClass Extends %Persistent
{
}
パッケージからスキーマへのプロジェクション
永続クラスの場合は、パッケージが SQL スキーマとして SQL で表現されます。例えば、クラスが Team.Player (Team パッケージの Player クラス) である場合、対応するテーブルは Team.Player (Team スキーマの Player テーブル) です。
既定のパッケージは User です。これが SQLUser スキーマとして SQL で表現されます。したがって、User.Person という名前のクラスは、SQLUser.Person という名前のテーブルに対応します。
パッケージ名にピリオドが含まれる場合、ピリオドの代わりにアンダースコア (_) を使用します。例えば、MyTest.Test.MyClass クラス (MyTest.Test パッケージの MyClass クラス) は、MyTest_Test.MyClass テーブル (MyTest_Test スキーマの MyClass テーブル) になります。
SQL テーブル名がスキーマ名なしで参照される場合、既定のスキーマ名 (SQLUser) が使用されます。例えば、以下のコマンドがあります。
Select ID, Name from Person
上記は、以下と同じです。
Select ID, Name from SQLUser.Person
永続クラスのテーブル名の指定
永続クラスの場合は、短いクラス名がテーブル名になります (既定)。
別のテーブル名を指定する場合は、SqlTableName クラス・キーワードを使用します。以下に例を示します。
Class App.Products Extends %Persistent [ SqlTableName = NewTableName ]
InterSystems IRIS には、クラス名に対する制限はありませんが、SQL 予約語を SQL テーブルの名前に使用することはできません。そのため、予約語の名前を付けた永続クラスを作成すると、クラス・コンパイラによりエラー・メッセージが生成されます。この場合は、ここで説明する方法を使用して、クラス名を変更するか、クラス名とは異なるテーブル名をプロジェクションに指定する必要があります。
ストレージ定義とストレージ・クラス
%PersistentOpens in a new tab クラスは、データベース内のオブジェクト保存と検索用の、高レベルのインタフェースを提供します。オブジェクトの保存とロードの実際の作業は、ストレージ・クラスによって実行されます。
すべての永続オブジェクトおよびすべてのシリアル・オブジェクトはストレージ・クラスを使用して、データベースのオブジェクトの保存、ロード、および削除に使用される実際のメソッドを生成します。これらの内部メソッドは、ストレージ・インタフェースと呼ばれます。ストレージ・インタフェースには、%LoadData()、%SaveData()、および %DeleteData() などのメソッドが含まれます。これらのメソッドはアプリケーションから直接呼ばれることはなく、永続インタフェース (%OpenId() や %Save() など) のメソッドによって適宜呼び出されます。
永続クラスによって使用されるストレージ・クラスは、ストレージ定義によって指定されます。ストレージ定義には、ストレージ・インタフェースによって使用される追加のパラメータや、ストレージ・クラスを定義する、一連のキーワードや値が含まれます。
永続クラスには、1 つ以上のストレージ定義を含みますが、一度に 1 つしかアクティブになりません。アクティブなストレージ定義は、クラスの StorageStrategy キーワードを使用して指定されます。既定では、永続クラスは Default と呼ばれるストレージ定義を 1 つ持っています。
クラスのデータを格納するグローバルの名前の詳細は、"グローバル" を参照してください。
ストレージ定義の更新
クラスのストレージ定義は、クラスを初めてコンパイルしたときに作成されます。SQL などのためのクラス・プロジェクションは、コンパイル後に実行されます。クラスが正しくコンパイルされ、その後、プロジェクションに失敗した場合、InterSystems IRIS は、ストレージ定義を削除しません。また、クラスの変更がストレージ定義に影響を及ぼす可能性がある場合、アプリケーション開発者の責任により、ストレージ定義が更新されているか判断し、必要に応じて、ストレージ定義を変更して変更を反映します。"ストレージ定義の再設定" を参照してください。
%Storage.Persistent ストレージ・クラス
%Storage.Persistent は、永続オブジェクトから使用される既定のストレージ・クラスです。これは、永続クラスに対する既定のストレージ構造を自動的に作成し、保持します。
新しい永続クラスは、自動的に %Storage.Persistent ストレージ・クラスを使用します。%Storage.Persistent クラスでは、ストレージ定義のさまざまなキーワードを使用して、クラスに使用されるストレージ構造の特定の機能を制御できます。
さまざまなストレージ・キーワードに関する詳細は、"クラス定義リファレンス" を参照してください。
また、MANAGEDEXTENT クラス・パラメータの詳細は、"エクステント定義" を参照してください。
Storage.SQL ストレージ・クラス
%Storage.SQL クラスは、生成された SELECT、INSERT、UPDATE、および DELETE の各 SQL 文を使用してオブジェクトの永続性を提供する特別なストレージ・クラスです。
%Storage.SQL は一般的に、以下の用途で使用されます。
-
従来のアプリケーションによって使用された、以前に存在したグローバル構造へのオブジェクトのマッピング
-
SQL ゲートウェイを使用した、外部リレーショナル・データベース内のオブジェクトの保存
%Storage.SQL は、%Storage.Persistent よりも制限されています。特に、複数のクラスのエクステントでのスキーマ進化の自動的なサポートを行いません。
スキーマ進化
%Storage.Persistent ストレージ・クラスは、自動的なスキーマ進化をサポートします。
既定の %Storage.Persistent ストレージ・クラスを使用する永続クラス (シリアル・クラス) をコンパイルするとき、クラス・コンパイラはクラスによって定義されたプロパティを解析し、自動的にそのプロパティを追加または削除します。
ストレージ定義の再設定
開発プロセス中は、永続クラスにプロパティの追加、変更、および削除などの変更をいくらでも加えることができます。クラス・コンパイラは互換性構造を維持しようと試みるため、ストレージ定義はより複雑になっていきます。すっきりしたストレージ構造がクラス・コンパイラで生成されるようにするには、クラスからストレージ定義を削除してから、そのクラスをリコンパイルします。
ID の生成を制御する方法
オブジェクトを初めて保存するときに、そのオブジェクトの ID はシステムによって生成されます。ID は永続的になります。
既定では、InterSystems IRIS は ID に整数を使用します。この整数は、最後に保存したオブジェクトから 1 だけ増分されます。
特定の永続クラスを定義する際に、以下のいずれかの方法で ID が生成されるように定義できます。
-
ID は、クラスの特定のプロパティを基にすることができます (そのプロパティがインスタンスごとに一意である場合)。例えば、ドラッグ・コードを ID として使用することも可能です。この方法でクラスを定義するには、以下のようなインデックスをクラスに追加します。
Index IndexName On PropertyName [ IdKey ];
または (同様に)
Index IndexName On PropertyName [ IdKey, Unique ];
IndexName はインデックスの名前、PropertyName はプロパティの名前です。
この方法でクラスを定義すると、InterSystems IRIS は最初にオブジェクトを保存するときに、そのプロパティの値を ID として使用するようになります。さらに、InterSystems IRIS はプロパティの値を要求して、そのプロパティの一意性を強制します。指定されたプロパティに同じ値を持つ別のオブジェクトを作成して、新しいオブジェクトを保存しようとすると、InterSystems IRIS は以下のエラーを発行します。
ERROR #5805: ID key not unique for extent
さらに、InterSystems IRIS により、そのプロパティは将来にわたって変更できなくなります。つまり、保存したオブジェクトを開き、そのプロパティ値を変更し、変更したオブジェクトを保存しようとすると、InterSystems IRIS は以下のエラーを発行するようになります。
ERROR #5814: Oid previously assigned
このメッセージは、ID ではなく OID について言及しています。これは、基礎となるロジックが OID の変更を防止しているためです。OID は ID を基にしています。
-
ID は複数のプロパティに基づいたものにできます。この方法でクラスを定義するには、以下のようなインデックスをクラスに追加します。
Index IndexName On (PropertyName1,PropertyName2,...) [ IdKey, Unique ];
または (同様に)
Index IndexName On (PropertyName1,PropertyName2,...) [ IdKey ];
IndexName はインデックスの名前、PropertyName1、PropertyName2 などはプロパティの名前です。
この方法でクラスを定義すると、InterSystems IRIS は最初にオブジェクトを保存するときに、以下のように ID を生成するようになります。
PropertyName1||PropertyName2||...
さらに、InterSystems IRIS はプロパティの値を要求して、プロパティの指定の組合せの一意性を強制します。さらに、それらのプロパティはいずれも将来にわたって変更できなくなります。
リテラル・プロパティ (つまり、属性) に、連続する 1 対の垂直バー (||) が含まれている場合、そのプロパティを使用する IdKey インデックスは追加しないでください。この制限は、InterSystems SQL のメカニズムが動作するための方法に起因しています。IdKey プロパティ で || を使用すると、予測できない動作を起こす場合があります。
システムは OID も生成します。いずれの場合でも、OID は以下の形式になります。
$LISTBUILD(ID,Classname)
ID は生成された ID です。また、Classname はクラスの名前です。
サブクラスの SQL プロジェクションの制御
スーパークラス/サブクラスの階層内に複数の永続クラスが存在する場合、InterSystems IRIS は、それらのデータを 2 通りの方法で保存します。既定のシナリオは、極めて一般的です。
サブクラスの既定の SQL プロジェクション
クラス・コンパイラは、永続クラスを平坦化した表現を投影します。投影されたテーブルには、そのクラスの該当するフィールドのすべて (継承されたフィールドを含む) が含まれるようになります。したがって、サブクラスの SQL プロジェクションは、以下の項目で構成されたテーブルになります。
-
スーパークラスのエクステントに含まれるすべての列
-
サブクラスだけにあるプロパティに基づいたその他の列
-
サブクラスの保存済みインスタンスを表す行
さらに、既定のシナリオでは、スーパークラスのエクステントには、スーパークラスおよびそのスーパークラスのすべてのサブクラスの保存済みオブジェクトごとに 1 つのレコードが格納されます。各サブクラスのエクステントは、スーパークラスのエクステントのサブセットです。
例えば、SAMPLES 内には、永続クラスの Sample.Person と Sample.Employee があるとします。Sample.Employee クラスは Sample.Person を継承し、いくつかのプロパティが追加されます。SAMPLES では、どちらのクラスもデータを保存しています。
-
Sample.Person の SQL プロジェクションは、Sample.Person クラスの適切なプロパティをすべて含んでいるテーブルになります。Sample.Person テーブルには、Sample.Person クラスの保存済みインスタンスごとに 1 つのレコード、および Sample.Employee クラスの保存済みインスタンスごとに 1 つのレコードが格納されています。
-
Sample.Employee テーブルには、Sample.Person と同じ列が含まれています。さらに、Sample.Employee クラスに固有の列も含まれています。Sample.Employee テーブルには、Sample.Employee クラスの保存済みインスタンスごとに 1 つのレコードが格納されています。
これを表示するには、以下の SQL クエリを使用します。まず、Sample.Person のすべてのインスタンスをリストして、それらのプロパティを表示します。
SELECT * FROM Sample.Person
2 番目のクエリでは、Sample.Employee のすべてのインスタンスと、それらのプロパティをリストします。
SELECT * FROM Sample.Employee
Sample.Person テーブルには、1 から 200 の範囲の ID を持つレコードが含まれることに注意してください。101 から 200 までの範囲に含まれる ID を持つレコードは従業員であり、Sample.Employee テーブルは同じ従業員 (同じ ID と同じ追加列を持つ従業員) を示します。Sample.Person テーブルには、2 つの見かけ上のグループのみが配置されています。これは、SAMPLES データベースが人為的な方法で構築されているためです。Sample.Person テーブルが移入された後で、Sample.Employee テーブルが移入されます。
一般的に、サブクラスのテーブルには親クラスよりも多数の列と、少数の行が含まれます。サブクラスが親クラスを拡張するとき、サブクラスは通常、プロパティを追加するので、サブクラス内にはより多くの列があります。サブクラスには親よりもサブクラスのインスタンスが少ないので、多くの場合行数も少なくなります。
サブクラスの代替の SQL プロジェクション
既定のプロジェクションは最も便利なものですが、場合によっては、代替の SQL プロジェクションを使用することが必要になります。このシナリオでは、各クラスに独自のエクステントがあります。この形式のプロジェクションを行うには、スーパークラスの定義に以下を含めます。
[ NoExtent ]
例 :
Class MyApp.MyNoExtentClass [ NoExtent ]
{
//class implementation
}
このクラスの各サブクラスは、独自のエクステントを受け取ります。
この方法でクラスを作成し、その他のクラスのプロパティとして使用する場合は、"バリエーション : CLASSNAME パラメータ" を参照してください。
データを格納した永続クラスの再定義
開発プロセス中にクラスを再定義することはよくあることです。既にクラスのサンプル・データを作成している場合は、以下の点に注意してください。
-
コンパイラはクラスのデータを格納するグローバルに影響しません。
実際には、クラス定義を削除してもクラス定義のデータ・グローバルはそのまま残されます。これらのグローバルが不要になった場合は、手動で削除してください。
-
クラスのプロパティを追加または削除して、クラスのストレージ定義は変更しない場合、そのクラスのデータにアクセスするコードはすべて、これまでと同様に機能します。"スキーマ進化" を参照してください。
-
クラスのストレージ定義を変更する場合、データにアクセスするコードがこれまでと同様に機能するかしないかは、変更の種類によって異なります。
-
シャード化されていないものからシャード化されているものに、またはその逆にクラスを変更した場合、既存のデータにアクセスできなくなることがあります。
-
プロパティ検証がより制限的になるようにプロパティ定義を変更した場合、検証に合格しなくなったオブジェクト (またはレコード) で作業するとエラーが発生します。例えば、プロパティの MAXLEN パラメータを小さくすると、長すぎることになったそのプロパティの値を持っているオブジェクトで作業したときに、検証エラーが発生します。