永続オブジェクトの概要
この章では、永続クラスを使用した作業時の理解に役立つ概念について説明します。
“永続オブジェクトを使用した作業” の章、“永続クラスの定義” の章、および “永続クラスのその他のオプション” の章も参照してください。
この章で示されているサンプルの一部は、Samples-Data サンプル (https://github.com/intersystems/Samples-DataOpens in a new tab) からのものです。(例えば) SAMPLES という名前の専用ネームスペースを作成し、そのネームスペースにサンプルをロードすることをお勧めします。 一般的な手順は、"InterSystems IRIS® で使用するサンプルのダウンロード" を参照してください。
永続クラス
永続クラスとは、%PersistentOpens in a new tab を継承するあらゆるクラスのことです。永続オブジェクトとは、そのようなクラスのインスタンスのことです。
%PersistentOpens in a new tab クラスは、%RegisteredObjectOpens in a new tab のサブクラスであるため、オブジェクト・クラスになります。前の章で説明したメソッドを提供することに加え、%PersistentOpens in a new tab クラスでは、永続インタフェースも定義します。これは、メソッドのセットです。これらのメソッドにより、データベースにオブジェクトを保存すること、データベースからオブジェクトをロードすること、オブジェクトを削除すること、および存在をテストすることができるようになります。
既定の SQL プロジェクションの概要
どのような永続クラスについても、コンパイラが SQL テーブル定義を生成します。この定義により、このドキュメントで説明するオブジェクト・インタフェースに加えて、保存されたデータに SQL でアクセスできます。
このテーブルには、保存されたオブジェクトごとに 1 つのレコードが格納されています。また、このテーブルは InterSystems SQL でクエリできます。以下に、Sample.Person テーブルのクエリ結果を示します。
以下のテーブルに、既定のプロジェクションをまとめます。
投影元 (オブジェクト・コンセプト) | 投影先 (リレーショナル・コンセプト) |
---|---|
パッケージ | スキーマ |
クラス | テーブル |
OID | ID フィールド |
データ型プロパティ | フィールド |
参照プロパティ | 参照フィールド |
埋め込みオブジェクト | 一連のフィールド |
リスト・プロパティ | リスト・フィールド |
配列プロパティ | 子テーブル |
ストリーム・プロパティ | BLOB |
インデックス | インデックス |
クラス・メソッド | ストアド・プロシージャ |
後続の章では、詳細情報を示し、変更可能な内容について説明します。
-
テーブル名と、そのテーブルが属するスキーマの名前の詳細は、“永続クラスの定義” を参照してください。
その章では、サブクラスのプロジェクションを制御する方法についても説明しています。
-
リテラル・プロパティのプロジェクションについての詳細は、“リテラル・プロパティの定義と使用” を参照してください。
-
コレクション・プロパティのプロジェクションについての詳細は、“コレクションを使用した作業” を参照してください。
-
ストリーム・プロパティのプロジェクションについての詳細は、“ストリームを使用した作業” を参照してください。
-
オブジェクト値プロパティのプロジェクションについての詳細は、“オブジェクト値プロパティの定義と使用” を参照してください。
-
リレーションシップのプロジェクションについての詳細は、“リレーションシップの定義と使用” を参照してください。
保存したオブジェクトの識別子 : ID および OID
初めてオブジェクトを保存するとき、システムは、2 つの永続識別子を作成します。どちらの識別子も保存したオブジェクトに後でアクセスする場合や、保存したオブジェクトを削除する場合に使用できます。最も広範に使用される識別子は、オブジェクト ID です。ID は、テーブル内で一意である単純なリテラル値です。既定では、システムは ID として使用する整数を生成します。
OID は、より一般的です。OID にはクラス名も含まれていて、データベース内で一意です。実際には、アプリケーションは OID 値を使用する必要はありません。ID 値で十分に機能します。
%PersistentOpens in a new tab クラスには、ID または OID を使用するメソッドがあります。ID は、%OpenId()、%ExistsId()、%DeleteId() などのメソッドを使用するときに指定します。OID は、%Open()、%Exists()、%Delete() などのメソッドに引数として指定します。つまり、引数として ID を使用するメソッドは、そのメソッドの名前に Id が含まれているということです。引数として OID を使用するメソッドは、そのメソッドの名前に Id が含まれていません。このようなメソッドは、ほとんど使用されません。
永続オブジェクトがそのデータベースに保存されると、その参照属性 (つまり、他の永続オブジェクトへの参照) の値はすべて OID 値として保存されます。OID を持たないオブジェクト属性については、オブジェクトのリテラル値が、その他のオブジェクト状態と共に保存されます。
オブジェクト ID から SQL へのプロジェクション
オブジェクトの ID は、対応する SQL テーブルで使用できます。可能な場合、InterSystems IRIS は ID というフィールド名を使用します。また、InterSystems IRIS には、どのフィールド名を使用しているかわからない場合でも、ID にアクセスする方法があります。このシステムは、以下のとおりです。
-
オブジェクト ID は、オブジェクトのプロパティではないため、プロパティとは異なる扱いを受けます。
-
ID という名前のプロパティ (どのような大文字小文字の組合せであっても) がクラスに含まれていない場合、テーブルにはフィールド ID も含まれるようになり、そのフィールドにオブジェクト ID が格納されます。この例については、前のセクションを参照してください。
-
ID という名前 (どのような大文字小文字の組合せであっても) を SQL に投影したプロパティがクラスに含まれている場合、テーブルにはフィールド ID1 も含まれるようになり、このフィールドにオブジェクト ID の値が保持されます。
同様に、ID および ID1 として投影されたプロパティがクラスに含まれている場合、テーブルにはフィールド ID2 も含まれるようになり、このフィールドにオブジェクト ID の値が保持されます。
-
すべての場合で、テーブルに疑似フィールド %ID も用意され、オブジェクト ID の値が保持されます。
OID は SQL テーブルでは使用できません。
SQL でのオブジェクト ID
InterSystems IRIS は、ID フィールドに一意性を強制します (実際の名前は、どのようなものであってもかまいません)。また、InterSystems IRIS は、このフィールドの変更を禁止します。つまり、このフィールドに対する SQL UPDATE または INSERT 操作は実行できないということです。例として、新しいレコードをテーブルに追加するために必要な SQL を以下に示します。
INSERT INTO PERSON (FNAME, LNAME)VALUES (:fname, :lname)
この SQL は ID フィールドを参照しません。InterSystems IRIS は、ID フィールドの値を生成し、要求されたレコードの作成時にその値を挿入します。
永続クラスに固有のクラス・メンバ
InterSystems IRIS クラスには、永続クラスでのみ意味のある数種のクラス・メンバを含めることができます。これに該当するものは、ストレージ定義、インデックス、外部キー、およびトリガです。
ストレージ定義
ほとんどの場合 (後述するように)、永続クラスごとにストレージ定義が存在します。ストレージ定義の用途は、グローバル構造を記述することです。InterSystems IRIS では、クラスのデータを保存するときや、クラスの保存データを読み込むときに、この構造を使用します。統合開発環境 (IDE) では、クラス定義の最後の部分にストレージ定義が表示されます。以下は、部分的な例です。
<Storage name="Default">
<Data name="PersonDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Name</Value>
</Value>
<Value name="3">
<Value>SSN</Value>
</Value>
<Value name="4">
<Value>DOB</Value>
</Value>
<Value name="5">
<Value>Home</Value>
</Value>
<Value name="6">
<Value>Office</Value>
</Value>
<Value name="7">
<Value>Spouse</Value>
</Value>
<Value name="8">
<Value>FavoriteColors</Value>
</Value>
</Data>
<DataLocation>^Sample.PersonD</DataLocation>
<DefaultData>PersonDefaultData</DefaultData>
<ExtentSize>200</ExtentSize>
<IdLocation>^Sample.PersonD</IdLocation>
<IndexLocation>^Sample.PersonI</IndexLocation>
<Property name="%%CLASSNAME">
<Selectivity>50.0000%</Selectivity>
</Property>
...
また、ほとんどの場合、コンパイラでもストレージ定義が生成および更新されます。永続クラスに使用するグローバルの詳細は、“グローバル” を参照してください。
インデックス
その他の SQL テーブルのように、InterSystems SQL テーブルはインデックスOpens in a new tabを保持できます。インデックスを定義するには、対応するクラス定義にインデックス定義を追加します。
インデックスにより、特定のフィールドまたはフィールドの組合せの一意性を確保する制約を追加できます。このようなインデックスの詳細は、“永続クラスの定義” の章を参照してください。
インデックスのもう 1 つの用途は、クエリの実行速度を速くするために、クラスに関連付けられ頻繁に要求されるデータのソート済み特定サブセットを定義することです。例えば、一般に、特定のフィールドを使用する WHERE 節がクエリに含まれている場合は、そのフィールドにインデックスが作成されていると、クエリの実行速度が速くなります。一方、そのフィールドにインデックスがない場合、エンジンは、すべての行に対して指定の条件と一致しているかどうかを確認するフル・テーブル・スキャンを実行する必要があります。これは、テーブルが巨大になると、負荷の高い操作になります。“永続クラスのその他のオプション” の章を参照してください。
外部キー
InterSystems SQL テーブルは、外部キーOpens in a new tabも保持できます。これを定義するには、対応するクラス定義に外部キー定義を追加します。
外部キーは、新しいデータが追加された場合や、データが変更された場合に InterSystems IRIS が使用するテーブル間に、参照整合性制約を確立します。このドキュメントで後述するリレーションシップを使用すると、システムはリレーションシップを自動的に外部キーとして扱います。リレーションシップを使用しない場合や、追加する理由が別にある場合は、外部キーを追加することもできます。
外部キーの詳細は、“永続クラスのその他のオプション” の章を参照してください。
トリガ
InterSystems SQL テーブルは、トリガOpens in a new tabも保持できます。これを定義するには、対応するクラス定義にトリガ定義を追加します。
トリガでは、特定のイベントの発生時 (具体的には、レコードの挿入時、変更時または削除時) に自動的に実行されるコードを定義します。
トリガの詳細は、“永続クラスのその他のオプション” の章を参照してください。
その他のクラス・メンバ
クラス・メソッドやクラス・クエリは、ストアド・プロシージャOpens in a new tabとして呼び出せるように定義できます。このストアド・プロシージャは SQL から呼び出せます。
この章で説明していないクラス・メンバの場合は、SQL への投影がありません。つまり、InterSystems IRIS には、それらのクラス・メンバを SQL から直接使用する方法や、SQL から簡単に使用できるようにする方法がないということです。
エクステント
エクステントという用語は、特定の永続クラスのすべてのレコード (ディスク上のレコード) を表します。次の章で示すように、%PersistentOpens in a new tab クラスには、クラスのエクステントを操作する複数のメソッドが用意されています。
InterSystems IRIS は、従来にない強力な解釈によるオブジェクトテーブル・マッピングを使用します。既定では、特定の永続クラスのエクステントには、すべてのサブクラスのエクステントが含まれます。したがって、以下のようになります。
-
永続クラス Person にサブクラス Employee がある場合、Person エクステントには、Person のすべてのインスタンスと Employee のすべてのインスタンスが含まれます。
-
クラス Employee のどのインスタンスの場合も、そのインスタンスは Person エクステントおよび Employee エクステントに含まれます。
インデックスは、インデックスが定義されるクラスの全範囲を自動的にカバーします。Person に定義されているインデックスには、Person インスタンスと Employee インスタンスの両方が含まれます。Employee エクステントで定義されたインデックスは、Employee インデックスだけを含みます。
サブクラスは、そのスーパークラスで定義されていない追加のプロパティも定義できます。これらは、サブクラスのエクステントでは使用できますが、スーパークラスのエクステントでは使用できません。例えば、Employee エクステントには Department フィールドが含まれますが、これは Person エクステントには含まれません。
前述の点は、InterSystems IRIS では、同じタイプのレコードをすべて取得するクエリを比較的簡単に作成できることを意味します。例えば、すべてのタイプの人々をカウントする場合、Person テーブルに対してクエリを実行できます。従業員のみをカウントする場合、同じクエリを Employee テーブルに対して実行します。これとは対照的に、他のオブジェクト・データベースでは、すべてのタイプの人々をカウントするには、テーブルを結合する複雑なクエリを作成する必要があり、別のサブクラスが追加されるたびにこのクエリを更新する必要があります。
同様に、ID を使用するメソッドは、すべて多様な形態で動作します。つまり、渡された ID 値に応じて、さまざまなタイプのオブジェクトを操作できるということです。
例えば、Sample.Person オブジェクトのエクステントには、Sample.Person のインスタンスと、Sample.Employee のインスタンスが含まれます。Sample.Person クラスの %OpenId() を呼び出すと、結果の OREF は、データベースに保存されている内容に応じて Sample.Person か Sample.Employee のどちらかのインスタンスになります。
// Open person "10"
Set obj = ##class(Sample.Person).%OpenId(10)
Write $ClassName(obj),! // Sample.Person
// Open person "110"
Set obj = ##class(Sample.Person).%OpenId(110)
Write $ClassName(obj),! // Sample.Employee
Sample.Employee クラスの %OpenId() メソッドでは、ID 10 を開こうとしても、オブジェクトが返されません。これは、ID 10 は Sample.Employee のエクステントではないためです。
// Open employee "10"
Set obj = ##class(Sample.Employee).%OpenId(10)
Write $IsObject(obj),! // 0
// Open employee "110"
Set obj = ##class(Sample.Employee).%OpenId(110)
Write $IsObject(obj),! // 1
エクステントの管理
エクステント定義
既定のストレージ・クラス (%Storage.Persistent) を使用するクラスの場合、InterSystems IRIS は、エクステント・マネージャで使用するために登録されているエクステントのエクステント定義とグローバルを管理します。エクステント・マネージャに対するインタフェースには、%ExtentMgr.UtilOpens in a new tab クラスを使用します。この登録プロセスはクラスのコンパイル時に実行されます。
何らかのエラーまたは名前の競合が発生すると、コンパイルに失敗します。以下に例を示します。
ERROR #5564: Storage reference: '^This.App.Global used in 'User.ClassA.cls' is already registered for use by 'User.ClassB.cls'
コンパイルを成功させるには競合を解決します。このユーティリティでは、インデックスの名前を変更するか、データのストレージの場所を明確に追加する必要があります。
グローバル参照を意図的に共有しているクラスがアプリケーションに複数存在する場合は、関連するすべてのクラスの MANAGEDEXTENT に 0 を指定します (関連するクラスが既定のストレージを使用する場合)。
MANAGEDEXTENT クラス・パラメータの既定値は 1 です。この値では、グローバル名の登録および競合使用のチェックが実行されます。値が 0 の場合には、登録も競合のチェックも実行されません。
別の可能性として、InterSystems IRIS に古いエクステント定義が含まれていることがあります。例えば、クラスを削除しても、既定ではエクステント定義は削除されません。
前述の例を引き続き使用すると、User.ClassA.cls は削除されましたが、そのエクステント定義が残っている可能性があります。この場合の解決策は、以下のコマンドを使用して、古いエクステント定義を削除することです。
set st = ##class(%ExtentMgr.Util).DeleteExtentDefinition("User.ClassA", "cls")
これで、競合することなく、User.ClassB.cls をコンパイルできます。
クラスとそのエクステント定義を削除するには、以下のいずれかの呼び出しを使用します。
-
$SYSTEM.OBJ.Delete(classname,qspec)。classname は、削除するクラスです。qspec では、フラグ e または修飾子 /deleteextent を指定します。
-
$SYSTEM.OBJ.DeletePackage(packagename,qspec)。packagename は、削除するパッケージです。qspec では、フラグ e または修飾子 /deleteextent を指定します。
-
$SYSTEM.OBJ.DeleteAll(qspec)。qspec では、フラグ e または修飾子 /deleteextent を指定します。これにより、ネームスペース内のすべてのクラスが削除されます。
これらの呼び出しは、%SYSTEM.OBJOpens in a new tab クラスのメソッドです。
エクステント・インデックス
エクステント・インデックスは、現在のネームスペースで定義されているすべてのエクステントおよびサブエクステントのインデックスです。クラス・コンパイラは、既定のストレージ・クラス (%Storage.Persistent) を使用する、ローカルでコンパイルされたクラスについてはこのインデックスを保持します。他のネームスペースからマップされたクラスは、マッピングが変更された場合やクラスのランタイムが元のネームスペースで変更された場合に自動的にインデックスに追加されたり、インデックスから削除されたりすることはありません。代わりに、このような変更は、以下のいずれかの呼び出しを使用して、ローカル・ネームスペースのエクステント・インデックスで更新する必要があります。
-
$SYSTEM.OBJ.RebuildExtentIndex(updatemode,lockmode) は、指定されたネームスペースのエクステント・インデックス全体を再構築し、成功または失敗を示すステータス値を返します。updatemode が true の場合、インデックスは削除されず、検出された差異のみがインデックスで更新されます。updatemode が false の場合は、インデックスが削除され、全体が再構築されます。lockmode が 1 の場合、インデックス構造全体に対して排他ロックが取得されます。値 2 の場合は、そのクラスのインデックス作成中に限り、クラス・ノードに対して個々のロックが取得されます。値 3 の場合は、インデックス構造全体に対して共有ロックが取得されます。要求されたロックを取得できない場合は、呼び出し元にエラーが報告されます。lockmode が 0 の場合、ロックなしを示します。
-
$SYSTEM.OBJ.RebuildExtentIndexOne(classname,lockmode) は、1 つのクラスのエクステント・インデックスを再構築し、成功または失敗を示すステータス値を返します。このメソッドは、classname によって指定されたクラスのエクステント・インデックスでインデックス・ノードを更新します。lockmode の値は RebuildExtentIndex() の場合と同じですが、値 1、2、および 3 は、個々のクラス・ノードに対するロックとして解釈されます。
これらの呼び出しは、%SYSTEM.OBJOpens in a new tab クラスのメソッドです。
Extent クエリ
すべての永続クラスには、"Extent" というクラス・クエリが自動的に含まれます。このクラス・クエリは、エクステントに含まれるすべての ID を提示します。
クラス・クエリの使用に関する一般的な情報は、“クラス・クエリの定義と使用” の章を参照してください。以下の例では、クラス・クエリを使用して、Sample.Person クラスのすべての ID を表示します。
set query = ##class(%SQL.Statement).%New()
set qStatus = query.%PrepareClassQuery("Sample.Person","Extent")
if $$$ISERR(qStatus) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(qStatus) quit}
set rset=query.%Execute()
if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
while rset.%Next()
{
write rset.%Get("ID"),!
}
if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
Sample.Person エクステントは、Sample.Person のすべてのインスタンスとそのサブクラスを含みます。これについての説明は、“永続クラスの定義” の章を参照してください。
"Extent" クエリは、以下の SQL クエリと同等です。
SELECT %ID FROM Sample.Person
これらのいずれの方法でも、返される ID 値の順序には依存できないことに注意してください。この要求を満たすために、他のプロパティ値を使用して順序付けられたインデックスを使用する方が効率的であると InterSystems IRIS が判断する場合があるからです。必要に応じて、ORDER BY %ID 節を SQL クエリに追加できます。
グローバル
永続クラスを使用することで、オブジェクトをデータベースに保存した後、オブジェクトとして取得することも、SQL を使用して取得することも可能になります。どのようにアクセスするかにかかわらず、これらのオブジェクトはグローバルに格納されます。グローバルは永続多次元配列と考えることができます。グローバルの操作の詳細は、"グローバルの使用法" を参照してください。
既定のストレージ・クラス (%Storage.Persistent) を使用するクラスを定義した場合、そのクラスをコンパイルすると、クラスのグローバル名が生成されます。これらの名前は、IDE で、コードの下部にあるストレージ定義で確認できます。
以降の各サグセクションでは、グローバルの既定の名前付け方式、パフォーマンスの向上を目的として短いグローバル名を生成する方法、グローバル名を直接制御する方法、および列指向のストレージを使用した場合にグローバルに発生する相違点について説明します。
標準のグローバル名
IDE でクラスを定義すると、そのクラスの名前に基づいてクラスのグローバル名が生成されます。
例えば、以下のように GlobalsTest.President クラスを定義するとします。
Class GlobalsTest.President Extends %Persistent
{
/// President's name (last,first)
Property Name As %String(PATTERN="1U.L1"",""1U.L");
/// Year of birth
Property BirthYear As %Integer;
/// Short biography
Property Bio As %Stream.GlobalCharacter;
/// Index for Name
Index NameIndex On Name;
/// Index for BirthYear
Index DOBIndex On BirthYear;
}
クラスのコンパイル後に、クラスの下部に以下のようなストレージ定義が生成されることを確認できます。
Storage Default
{
<Data name="PresidentDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Name</Value>
</Value>
<Value name="3">
<Value>BirthYear</Value>
</Value>
<Value name="4">
<Value>Bio</Value>
</Value>
</Data>
<DataLocation>^GlobalsTest.PresidentD</DataLocation>
<DefaultData>PresidentDefaultData</DefaultData>
<IdLocation>^GlobalsTest.PresidentD</IdLocation>
<IndexLocation>^GlobalsTest.PresidentI</IndexLocation>
<StreamLocation>^GlobalsTest.PresidentS</StreamLocation>
<Type>%Storage.Persistent</Type>
}
特に、次のストレージ・キーワードに着目してください。
-
DataLocation は、クラス・データが格納されるグローバルです。このグローバルの名前は、"D" が追加された完全なクラス名 (パッケージ名を含む) で、ここでは、^GlobalsTest.PresidentD となっています。
-
IdLocation (DataLocation と同じである場合が多い) は、ID カウンタが格納されるグローバルで、ルートにあります。
-
IndexLocation は、クラスのインデックスが格納されるグローバルです。このグローバルの名前は、"I" が追加された完全なクラス名で、^GlobalsTest.PresidentI となっています。
-
StreamLocation は、あらゆるストリーム・プロパティが格納されるグローバルです。このグローバルの名前は、"S" が追加された完全なクラス名で、^GlobalsTest.PresidentS となっています。
クラスのオブジェクトをいくつか作成して保存した後は、ターミナルでこれらのグローバルのコンテンツを確認できます。
USER>zwrite ^GlobalsTest.PresidentD
^GlobalsTest.PresidentD=3
^GlobalsTest.PresidentD(1)=$lb("",1732,"1","Washington,George")
^GlobalsTest.PresidentD(2)=$lb("",1735,"2","Adams,John")
^GlobalsTest.PresidentD(3)=$lb("",1743,"3","Jefferson,Thomas")
USER>zwrite ^GlobalsTest.PresidentI
^GlobalsTest.PresidentI("DOBIndex",1732,1)=""
^GlobalsTest.PresidentI("DOBIndex",1735,2)=""
^GlobalsTest.PresidentI("DOBIndex",1743,3)=""
^GlobalsTest.PresidentI("NameIndex"," ADAMS,JOHN",2)=""
^GlobalsTest.PresidentI("NameIndex"," JEFFERSON,THOMAS",3)=""
^GlobalsTest.PresidentI("NameIndex"," WASHINGTON,GEORGE",1)=""
USER>zwrite ^GlobalsTest.PresidentS
^GlobalsTest.PresidentS=3
^GlobalsTest.PresidentS(1)="1,239"
^GlobalsTest.PresidentS(1,1)="George Washington was born to a moderately prosperous family of planters in colonial ..."
^GlobalsTest.PresidentS(2)="1,195"
^GlobalsTest.PresidentS(2,1)="John Adams was born in Braintree, Massachusetts, and entered Harvard College at age 1..."
^GlobalsTest.PresidentS(3)="1,202"
^GlobalsTest.PresidentS(3,1)="Thomas Jefferson was born in the colony of Virginia and attended the College of Willi..."
^GlobalsTest.PresidentD の添え字は IDKey です。いずれのインデックスも IDKey としては定義していないため、ID が IDKey として使用されます。ID の詳細は、“ID の生成を制御する方法” を参照してください。
^GlobalsTest.PresidentI の最初の添え字はインデックスの名前です。
^GlobalsTest.PresidentS の最初の添え字は、大統領の ID ではなく、略歴エントリの ID です。
これらのグローバルは、管理ポータルでも確認できます ([システムエクスプローラ]→[グローバル])。
グローバル名では最初の 31 文字だけが重要であるため、完全なクラス名がきわめて長い場合は、^package1.pC347.VeryLongCla4F4AD のようなグローバル名が生成される場合があります。クラスのすべてのグローバル名が必ず一意になるように、このような名前が生成されます。クラスのグローバルで直接作業する場合、ストレージ定義を調べて、グローバルの実際の名前を確認してください。または、クラス定義内で DEFAULTGLOBAL パラメータを使用して、グローバル名を制御できます。“ユーザ定義のグローバル名” を参照してください。
ハッシュ化したグローバル名
USEEXTENTSET パラメータを値 1 に設定している場合、短いグローバル名が生成されます (このパラメータの既定値は 0、つまり標準のグローバル名の使用です)。こういった短いグローバル名は、パッケージ名のハッシュとクラス名のハッシュから作成され、後ろに接尾辞が付きます。標準の名前の方が読みやすいですが、短い名前の方がパフォーマンスの向上につながる可能性があります。
USEEXTENTSET を 1 に設定した場合、各インデックスも個別のグローバルに割り当てられます。最初の添え字が異なる単一のインデックス・グローバルは使用されません。繰り返しますが、これはパフォーマンス向上のために行われます。
前述の箇所で定義した GlobalsTest.President クラスにハッシュ化したグローバル名を使用するには、クラス定義に以下を追加します。
/// Use hashed global names
Parameter USEEXTENTSET = 1;
ストレージ定義を削除し、クラスをリコンパイルした後に、ハッシュ化されたグローバル名が含まれる以下のような新しいストレージ定義を確認できます。
Storage Default
{
...
<DataLocation>^Ebnm.EKUy.1</DataLocation>
<DefaultData>PresidentDefaultData</DefaultData>
<ExtentLocation>^Ebnm.EKUy</ExtentLocation>
<IdLocation>^Ebnm.EKUy.1</IdLocation>
<Index name="DOBIndex">
<Location>^Ebnm.EKUy.2</Location>
</Index>
<Index name="IDKEY">
<Location>^Ebnm.EKUy.1</Location>
</Index>
<Index name="NameIndex">
<Location>^Ebnm.EKUy.3</Location>
</Index>
<IndexLocation>^Ebnm.EKUy.I</IndexLocation>
<StreamLocation>^Ebnm.EKUy.S</StreamLocation>
<Type>%Storage.Persistent</Type>
}
特に、次のストレージ・キーワードに着目してください。
-
ExtentLocation は、このクラスのグローバル名の計算に使用されるハッシュ値です (ここでは ^Ebnm.EKUy)。
-
クラス・データが格納される DataLocation (IDKEY インデックスと等しい) は、名前に “.1” が付いたハッシュ値になっています (ここでは ^Ebnm.EKUy.1)。
-
各インデックスに固有の Location が指定されるため、各インデックスのグローバルも固有になります。IdKey インデックス・グローバルの名前は、名前に ”.1” が付いたハッシュ値と等しいです。この例では、^Ebnm.EKUy.1 です。これ以外のインデックスのグローバルでは、名前に “.2” から “.N” が付けられます。ここでは、DOBIndex はグローバル ^Ebnm.EKUy.2 に格納され、NameIndex は ^Ebnm.EKUy.3 に格納されます。
-
IndexLocation は、名前に “.I” が付いたハッシュ値、つまり ^Ebnm.EKUy.I ですが、このグローバルは多くの場合使用されません。
-
StreamLocation は、名前に “.S” が付いたハッシュ値、つまり ^Ebnm.EKUy.S です。
オブジェクトをいくつか作成して保存した後、これらのグローバルのコンテンツは以下のようになります (ここでもターミナルを使用して確認します)。
USER>zwrite ^Ebnm.EKUy.1
^Ebnm.EKUy.1=3
^Ebnm.EKUy.1(1)=$lb("","Washington,George",1732,"1")
^Ebnm.EKUy.1(2)=$lb("","Adams,John",1735,"2")
^Ebnm.EKUy.1(3)=$lb("","Jefferson,Thomas",1743,"3")
USER>zwrite ^Ebnm.EKUy.2
^Ebnm.EKUy.2(1732,1)=""
^Ebnm.EKUy.2(1735,2)=""
^Ebnm.EKUy.2(1743,3)=""
USER>zwrite ^Ebnm.EKUy.3
^Ebnm.EKUy.3(" ADAMS,JOHN",2)=""
^Ebnm.EKUy.3(" JEFFERSON,THOMAS",3)=""
^Ebnm.EKUy.3(" WASHINGTON,GEORGE",1)=""
USER>zwrite ^Ebnm.EKUy.S
^Ebnm.EKUy.S=3
^Ebnm.EKUy.S(1)="1,239"
^Ebnm.EKUy.S(1,1)="George Washington was born to a moderately prosperous family of planters in colonial ..."
^Ebnm.EKUy.S(2)="1,195"
^Ebnm.EKUy.S(2,1)="John Adams was born in Braintree, Massachusetts, and entered Harvard College at age 1..."
^Ebnm.EKUy.S(3)="1,202"
^Ebnm.EKUy.S(3,1)="Thomas Jefferson was born in the colony of Virginia and attended the College of Willi..."
SQL 文 CREATE TABLE を使用して定義したクラスの場合、USEEXTENTSET パラメータの既定値は 1 です。テーブルの作成の詳細は、"InterSystems SQL の使用法" の “テーブルの定義” を参照してください。
例えば、管理ポータルを使用してテーブルを作成してみます ([システムエクスプローラ]→[SQL]→[クエリ実行])。
CREATE TABLE GlobalsTest.State (NAME CHAR (30) NOT NULL, ADMITYEAR INT)
テーブルにいくつかのデータを入力した後、管理ポータル ([システムエクスプローラ]→[グローバル]) でグローバル ^Ebnm.BndZ.1 および ^Ebnm.BndZ.2 を確認します。パッケージ名は GlobalsTest のままなので、GlobalsTest.State のグローバル名の最初のセグメントは、GlobalsTest.President の場合と同じです。
ターミナルで、グローバルのコンテンツは以下のように表示されます。
USER>zwrite ^Ebnm.BndZ.1
^Ebnm.BndZ.1=3
^Ebnm.BndZ.1(1)=$lb("Delaware",1787)
^Ebnm.BndZ.1(2)=$lb("Pennsylvania",1787)
^Ebnm.BndZ.1(3)=$lb("New Jersey",1787)
USER>zwrite ^Ebnm.BndZ.2
^Ebnm.BndZ.2(1)=$zwc(412,1,0)/*$bit(2..4)*/
グローバル ^Ebnm.BndZ.1 には州のデータ、^Ebnm.BndZ.2 にはビットマップ・エクステント・インデックスが格納されています。"InterSystems SQL リファレンス" の “ビットマップ・エクステント・インデックス” を参照してください。
SQL を使用して作成したクラスで標準のグローバル名を使用する場合、以下のように USEEXTENTSET パラメータを 0 に設定できます。
CREATE TABLE GlobalsTest.State (%CLASSPARAMETER USEEXTENTSET 0, NAME CHAR (30) NOT NULL, ADMITYEAR INT)
これによって、標準のグローバル名 ^GlobalsTest.StateD および ^GlobalsTest.StateI が生成されます。
コマンド do $SYSTEM.SQL.SetDDLUseExtentSet(0, .oldval) を実行することによって、CREATE TABLE 文によって作成されたクラスについて USEEXTENTSET パラメータに使用される既定値を 0 に変更することができます。以前の既定値は oldval で返されます。
XEP を使用して作成したクラスの場合、USEEXTENTSET パラメータの既定値は 1 で、これを変更することはできません。XEP の詳細は、"InterSystems XEP による Java オブジェクトの永続化" を参照してください。
ユーザ定義のグローバル名
クラスのグローバル名をより詳細に制御するには、DEFAULTGLOBAL パラメータを使用します。このパラメータは、USEEXTENTSET パラメータと共に機能して、グローバル名前付け方式を決定します。
例えば以下のように、DEFAULTGLOBAL パラメータを追加して、GlobalsTest.President クラスのグローバル名のルートを ^GT.Pres に設定してみます。
/// Use hashed global names
Parameter USEEXTENTSET = 1;
/// Set the root of the global names
Parameter DEFAULTGLOBAL = "^GT.Pres";
ストレージ定義を削除し、クラスをリコンパイルした後に、以下のようなグローバル名を確認できます。
Storage Default
{
...
<DataLocation>^GT.Pres.1</DataLocation>
<DefaultData>PresidentDefaultData</DefaultData>
<ExtentLocation>^GT.Pres</ExtentLocation>
<IdLocation>^GT.Pres.1</IdLocation>
<Index name="DOBIndex">
<Location>^GT.Pres.2</Location>
</Index>
<Index name="IDKEY">
<Location>^GT.Pres.1</Location>
</Index>
<Index name="NameIndex">
<Location>^GT.Pres.3</Location>
</Index>
<IndexLocation>^GT.Pres.I</IndexLocation>
<StreamLocation>^GT.Pres.S</StreamLocation>
<Type>%Storage.Persistent</Type>
}
同様に、DEFAULTGLOBAL パラメータは、SQL を使用したクラスの定義にも使用できます。
CREATE TABLE GlobalsTest.State (%CLASSPARAMETER USEEXTENTSET 0, %CLASSPARAMETER DEFAULTGLOBAL = '^GT.State',
NAME CHAR (30) NOT NULL, ADMITYEAR INT)
これによって、グローバル名 ^GT.StateD および ^GT.StateI が生成されます。
列指向ストレージのグローバル
列指向ストレージは、リリース 2022.2 の試験的機能として利用できます。つまり、この機能は実稼働環境ではサポートされていません。今後のリリースへアップグレードした場合は、列指向テーブルのデータを再読み込みする必要があります。
以下のクラス定義は、基本的に "標準のグローバル名" で取り上げている定義と同じですが、クラス・パラメータ STORAGEDEFAULT = "columnar" がある点が異なります。このパラメータは、そのクラスの既定のストレージ・レイアウトとして列指向ストレージを使用することを指定します。つまり、データを行単位で格納 (President の各プロパティを 1 行に格納) するのではなく、クラスの各プロパティのデータを 1 つの列に格納します。
Class ColumnarTest.President Extends %Persistent [ DdlAllowed, Final ]
{
/// Specify columnar storage at the class level
Parameter STORAGEDEFAULT = "columnar"
/// President's name (last,first)
Property Name As %String(PATTERN="1U.L1"",""1U.L");
/// Year of birth
Property BirthYear As %Integer;
/// Short biography
Property Bio As %Stream.GlobalCharacter;
/// Index for Name
Index NameIndex On Name;
/// Index for BirthYear
Index DOBIndex On BirthYear;
}
このクラスをコンパイルすると、以下のようなストレージ定義が得られます。
Storage Default
{
<Data name="_CDM_Bio">
<Attribute>Bio</Attribute>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_BirthYear">
<Attribute>BirthYear</Attribute>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_Name">
<Attribute>Name</Attribute>
<Structure>vector</Structure>
</Data>
<DataLocation>^ColumnarTest.PresidentD</DataLocation>
<IdLocation>^ColumnarTest.PresidentD</IdLocation>
<IndexLocation>^ColumnarTest.PresidentI</IndexLocation>
<StreamLocation>^ColumnarTest.PresidentS</StreamLocation>
<Type>%Storage.Persistent</Type>
}
このストレージ定義は、従来の行ベースのストレージ・レイアウトを使用する標準のクラス定義に対して作成されたストレージ定義とは若干異なります。グローバルの名前は同じですが、列指向ストレージを使用したプロパティごとに <Data> 要素があります ("CDM" は列指向のデータ・マップを表します)。<Data> タグの name 属性は ^ColumnarTest.PresidentI グローバルの添え字として使用されています。<Attribute> 要素にはプロパティ名が記述され、<Structure> 要素の vector の値は、プロパティの値を列指向形式で表すためにベクトルが使用されることを示します。
新しいクラスのオブジェクトをいくつか追加すると、このグローバルは以下のようになります。
USER>zwrite ^ColumnarTest.PresidentD
^ColumnarTest.PresidentD=3
^ColumnarTest.PresidentD(1)=""
^ColumnarTest.PresidentD(2)=""
^ColumnarTest.PresidentD(3)=""
USER>zwrite ^ColumnarTest.PresidentI
^ColumnarTest.PresidentI("$President",1)=$zwc(412,1,0)/*$bit(2..4)*/
^ColumnarTest.PresidentI("DOBIndex",1732,1)=""
^ColumnarTest.PresidentI("DOBIndex",1735,2)=""
^ColumnarTest.PresidentI("DOBIndex",1743,3)=""
^ColumnarTest.PresidentI("NameIndex"," ADAMS,JOHN",2)=""
^ColumnarTest.PresidentI("NameIndex"," JEFFERSON,THOMAS",3)=""
^ColumnarTest.PresidentI("NameIndex"," WASHINGTON,GEORGE",1)=""
^ColumnarTest.PresidentI("_CDM_Bio",1)={"type":"integer", "count":3, "length":4, "vector":[,1,2,3]} ; <VECTOR>
^ColumnarTest.PresidentI("_CDM_BirthYear",1)={"type":"integer", "count":3, "length":4, "vector":[,1732,1735,1743]} ; <VECTOR>
^ColumnarTest.PresidentI("_CDM_Name",1)={"type":"string", "count":3, "length":4, "vector":[,"Washington,George","Adams,John","Jefferson,Thomas"]} ; <VECTOR>
USER>zwrite ^GlobalsTest.PresidentS
^ColumnarTest.PresidentS=3
^ColumnarTest.PresidentS(1)="1,239"
^ColumnarTest.PresidentS(1,1)="George Washington was born to a moderately prosperous family of planters in colonial ..."
^ColumnarTest.PresidentS(2)="1,195"
^ColumnarTest.PresidentS(2,1)="John Adams was born in Braintree, Massachusetts, and entered Harvard College at age 1..."
^ColumnarTest.PresidentS(3)="1,202"
^ColumnarTest.PresidentS(3,1)="Thomas Jefferson was born in the colony of Virginia and attended the College of Willi..."
ここで、^ColumnarTest.PresidentI("$President") はビットマップ・エクステント・インデックスです。
列指向のストレージを使用したプロパティと行ベースのプロパティが混在する "混合ストレージ" を使用するクラスの場合、行ベースのストレージを使用するプロパティは、標準のクラス定義の場合と同様にデータ・グローバル (^ColumnarTest.PresidentD など) に格納されます。上記の例では、すべてのプロパティで列指向ストレージが使用されています。STORAGEDEFAULT = "columnar" が、プロパティ・レベルではなく、クラス・レベルで指定されているからです。列指向のストレージをプロパティ・レベルで指定したクラス定義の例は、"列指向のストレージの使用法" を参照してください。
ColumnarTest.President クラスにハッシュ化したグローバル名を使用するには、以下のクラス・パラメータを使用します。
/// Use hashed global names
Parameter USEEXTENTSET = 1;
このクラスをコンパイルすると、以下のようなストレージ定義が得られます。
Storage Default
{
<Data name="_CDM_Bio">
<Attribute>Bio</Attribute>
<ColumnarGlobal>^IEA0.EKUy.1.V1</ColumnarGlobal>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_BirthYear">
<Attribute>BirthYear</Attribute>
<ColumnarGlobal>^IEA0.EKUy.1.V2</ColumnarGlobal>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_Name">
<Attribute>Name</Attribute>
<ColumnarGlobal>^IEA0.EKUy.1.V3</ColumnarGlobal>
<Structure>vector</Structure>
</Data>
<DataLocation>^IEA0.EKUy.1</DataLocation>
<ExtentLocation>^IEA0.EKUy</ExtentLocation>
<IdLocation>^IEA0.EKUy.1</IdLocation>
<Index name="$President">
<Location>^IEA0.EKUy.2</Location>
</Index>
<Index name="DOBIndex">
<Location>^IEA0.EKUy.3</Location>
</Index>
<Index name="IDKEY">
<Location>^IEA0.EKUy.1</Location>
</Index>
<Index name="NameIndex">
<Location>^IEA0.EKUy.4</Location>
</Index>
<IndexLocation>^IEA0.EKUy.I</IndexLocation>
<StreamLocation>^IEA0.EKUy.S</StreamLocation>
<Type>%Storage.Persistent</Type>
}
このストレージ定義は、上記の列指向ストレージの例と、ハッシュ化したグローバル名の例を組み合わせたような定義です。ここでは、このクラス定義の各プロパティの <Data> 要素に <ColumnarGlobal> 属性があります。この属性を使用して、列指向形式でそのプロパティの値を格納するために使用するグローバルの名前を指定します。$President インデックスには、ビットマップ・エクステント・インデックスが格納されます。
新しいクラスのオブジェクトをいくつか追加すると、このグローバルは以下のようになります。
USER>zwrite ^IEA0.EKUy.1.V1
^IEA0.EKUy.1.V1(1)={"type":"integer", "count":3, "length":4, "vector":[,1,2,3]} ; <VECTOR>
USER>zwrite ^IEA0.EKUy.1.V2
^IEA0.EKUy.1.V2(1)={"type":"integer", "count":3, "length":4, "vector":[,1732,1735,1743]} ; <VECTOR>
USER>zwrite ^IEA0.EKUy.1.V3
^IEA0.EKUy.1.V3(1)={"type":"string", "count":3, "length":4, "vector":[,"Washington,George","Adams,John","Jefferson,Thomas"]} ; <VECTOR>
USER>zwrite ^IEA0.EKUy.1
^IEA0.EKUy.1=3
^IEA0.EKUy.1(1)=""
^IEA0.EKUy.1(2)=""
^IEA0.EKUy.1(3)=""
USER>zwrite ^IEA0.EKUy.2
^IEA0.EKUy.2(1)=$zwc(412,1,0)/*$bit(2..4)*/
USER>zwrite ^IEA0.EKUy.3
^IEA0.EKUy.3(1732,1)=""
^IEA0.EKUy.3(1735,2)=""
^IEA0.EKUy.3(1743,3)=""
USER>zwrite ^IEA0.EKUy.4
^IEA0.EKUy.4(" ADAMS,JOHN",2)=""
^IEA0.EKUy.4(" JEFFERSON,THOMAS",3)=""
^IEA0.EKUy.4(" WASHINGTON,GEORGE",1)=""
USER>zwrite ^IEA0.EKUy.S
^IEA0.EKUy.S=3
^IEA0.EKUy.S(1)="1,239"
^IEA0.EKUy.S(1,1)="George Washington was born to a moderately prosperous family of planters in colonial ..."
^IEA0.EKUy.S(2)="1,195"
^IEA0.EKUy.S(2,1)="John Adams was born in Braintree, Massachusetts, and entered Harvard College at age 1..."
^IEA0.EKUy.S(3)="1,202"
^IEA0.EKUy.S(3,1)="Thomas Jefferson was born in the colony of Virginia and attended the College of Willi..."
グローバル名の再定義
既存のグローバル名を再定義する方法 (例えば、USEEXTENTSET パラメータまたは DEFAULTGLOBAL パラメータの値の変更) でクラス定義を編集する場合、既存のストレージ定義を削除して、コンパイラで新しいストレージ定義を生成できるようにする必要があります。既存のグローバル内のデータはすべて保持されます。保持されるデータをすべて新しいグローバル構造に移行する必要があります。
詳細は、“データを格納した永続クラスの再定義” を参照してください。