永続オブジェクトとグローバル
永続クラスを使用することで、オブジェクトをデータベースに保存した後、オブジェクトとして取得することも、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 です。テーブルの作成の詳細は、"テーブルの定義" を参照してください。
例えば、管理ポータルを使用してテーブルを作成してみます ([システムエクスプローラ]→[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 にはビットマップ・エクステント・インデックスが格納されています。"ビットマップ・エクステント・インデックス" を参照してください。
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 が生成されます。
列指向ストレージのグローバル
以下のクラス定義は、基本的に "標準のグローバル名" で取り上げている定義と同じですが、クラス・パラメータ 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 パラメータの値の変更) でクラス定義を編集する場合、既存のストレージ定義を削除して、コンパイラで新しいストレージ定義を生成できるようにする必要があります。既存のグローバル内のデータはすべて保持されます。保持されるデータをすべて新しいグローバル構造に移行する必要があります。
詳細は、"データを格納した永続クラスの再定義" を参照してください。