Skip to main content

This is documentation for Caché & Ensemble. See the InterSystems IRIS version of this content.Opens in a new tab

For information on migrating to InterSystems IRISOpens in a new tab, see Why Migrate to InterSystems IRIS?

永続オブジェクトを使用した作業

%PersistentOpens in a new tab クラスは、保存可能な (ディスクに書き込める) オブジェクトに対応した API です。この章では、この API の使用方法について説明します。この章の情報は、%PersistentOpens in a new tab のすべてのサブクラスに適用されます。以下のトピックについて説明します。

永続オブジェクトの概要” の章、“永続クラスの定義” の章、および “永続クラスのその他のオプション” の章も参照してください。

このドキュメントをオンラインで表示している場合は、このドキュメントの "序文" を使用すると、他のトピックをすばやく見つけることができます。

オブジェクトの保存

オブジェクトをデータベースに保存するには、そのオブジェクトの %Save() メソッドを呼び出します。以下に例を示します。

 Set obj = ##class(MyApp.MyClass).%New()
 Set obj.MyValue = 10

 Set sc = obj.%Save()

%Save() メソッドは、保存操作が成功したか失敗したかを示す %StatusOpens in a new tab 値を返します。例えば、オブジェクトが無効なプロパティ値を持つ場合や一意性に違反した場合、エラーが発生することがあります。前の章の “オブジェクトの検証” を参照してください。

オブジェクトで %Save() を呼び出すと、保存されているオブジェクトから “アクセス” できる、すべての変更済みオブジェクトが自動的に保存されます。つまり、すべての埋め込みオブジェクト、コレクション、ストリーム、参照オブジェクト、およびオブジェクトに関与するリレーションシップが必要に応じて自動的に保存されます。保存操作全体が 1 つのトランザクションとして実行されます。保存できないオブジェクトがある場合、トランザクション全体が失敗し、ロール・バックされます (ディスクには変更がなく、すべてのメモリ内オブジェクト値が、%Save() メソッドが呼び出される前の値に戻ります)。

初めてオブジェクトが保存されるとき、%Save() メソッドの既定の動作は、自動的にそのオブジェクトにオブジェクト ID 値を割り当てます。このID 値は後に、データベース内でオブジェクトの検索時に使用されます。既定の場合、ID は、$Increment 関数を使用して生成されます。または、クラスは、idkey index を持つプロパティ値に基づくユーザ提供オブジェクト ID を使用できます (この場合、プロパティ値は、 “||” 文字列を含むことはできません)。一度割り当てられると、(これがユーザ提供の ID でも) 特定のオブジェクト・インスタンスに対するオブジェクト ID 値を変更できません。

%Id() メソッドを使用して、保存されたオブジェクトに対するオブジェクト ID を検索できます。

 // Open person "22"
 Set person = ##class(Sample.Person).%OpenId(22)
 Write "Object ID: ",person.%Id(),!

%Save() メソッドが行う詳細は、以下のとおりです。

  1. 最初に、“SaveSet” として知られる一時的な構造を構築します。SaveSet は単純なグラフで、保存されたオブジェクトから到達可能なすべてのオブジェクトに対する参照を含みます。(一般に、オブジェクト・クラス A に、別のオブジェクト・クラス B の値を保持しているプロパティがあれば、A のインスタンスは B のインスタンスに “到達” できます。) SaveSet の目的は、関連するオブジェクトの複雑なセットに関連する保存操作が、可能な限り効果的に実行できるようにすることです。SaveSet は、オブジェクト間の保存順序の依存関係も解決します。

    各オブジェクトが SaveSet に追加されると、その %OnAddToSaveSet() コールバック・メソッドが存在する場合は呼び出されます。

  2. SaveSet 内の各オブジェクトを順番に検索し、それらが変更されていないかをチェックします (つまり、オブジェクトを開いた後で、または最後に保存されて以降、そのプロパティ値が変更されていないかをチェックします)。変更されているオブジェクトがある場合、そのオブジェクトは保存されます。

  3. 保存される前に、変更された各オブジェクトは検証されます (そのプロパティ値がテストされます。その %OnValidateObject() メソッドが存在する場合は呼び出され、一意性制約がテストされます)。オブジェクトが有効な場合、保存操作が継続します。無効なオブジェクトがある場合は、%Save() の呼び出しが失敗し、現在のトランザクションがロール・バックされます。

  4. 各オブジェクトの保存前、または保存後、%OnBeforeSave() メソッドと %OnAfterSave() コールバック・メソッドが存在する場合は実行されます。

    これらのコールバックに Insert 引数が渡されます。この引数は、オブジェクトが挿入されているか (初めての保存の場合)、または更新されているかを示すものです。

    これらのコールバック・メソッドのいずれかが失敗した場合、%Save() への呼び出しは失敗し、現在のトランザクションがロール・バックされます。

現在のオブジェクトが変更されていない場合、ディスクには %Save() メソッドによって書き込みは行われません。オブジェクトの保存は必要がなく、そのため保存は失敗しないため、成功が返されます。 実際のところ、%Save() の返り値では、保存操作で要求がすべて実行されたか、それとも要求どおりには実行されなかったか (および明確にではなく何かがディスクに書き込まれたかどうか) が示されます。

Important:

マルチプロセス環境では、適切な並行処理の制御を使用していることを確認してください。“オブジェクト同時処理のオプション” を参照してください。

Rollback

%Save() メソッドは、SaveSet 内のすべてのオブジェクトを単独のトランザクションとして自動的に保存します。これらのオブジェクトのいずれかの保存が失敗した場合、トランザクション全体がロール・バックされます。このロールバックの場合、Caché は以下を実行します。

  1. 割り当てられた ID を元に戻します。

  2. 削除された ID を回復します。

  3. 変更されたビットを回復します。

  4. 正常にシリアル化されたオブジェクトに対して、%OnRollBack() コールバック・メソッドを呼び出します (実装されている場合)。

    Caché は、正常にシリアル化されていないオブジェクト (つまり、無効なオブジェクト) に対しては、このメソッドを呼び出しません。

オブジェクトとトランザクションの保存

前述したように、%Save() メソッドは、SaveSet 内のすべてのオブジェクトを単独のトランザクションとして自動的に保存します。これらのオブジェクトのいずれかの保存が失敗した場合、トランザクション全体がロール・バックされます。

複数の関連しないオブジェクトを 1 つのトランザクションとして保存する場合は、%Save() の呼び出しを 1 つの明示的なトランザクション内にまとめる必要があります。つまり、TSTART コマンドを使用してトランザクションを開始し、TCOMMIT コマンドを使用してトランザクションを終了する必要があります。

例 :

 // start a transaction
 TSTART

 // save first object
 Set sc = obj1.%Save()

 // save second object (if first was save)
 If ($$$ISOK(sc)) {
     Set sc = obj2.%Save()
 }

 // if both saves are ok, commit the transaction
 If ($$$ISOK(sc)) {
     TCOMMIT
 }

上記の例では、注意すべきことが 2 つあります。

  1. %Save() メソッドは、トランザクション内で呼び出されたかどうかを知っています (システム変数 $TLEVEL が 0 より大きいため)。

  2. トランザクション内の %Save() メソッドが失敗した場合、トランザクション全体がロール・バックされます (TROLLBACK コマンドが実行されます)。これは、アプリケーションは明示的なトランザクション内のすべての %Save() をテストし、いずれか 1 つが失敗したら他のオブジェクトでの %Save() の呼び出しをスキップし、最終的な TCOMMIT コマンドの呼び出しもスキップする必要があることを意味しています。

保存したオブジェクトの存在のテスト

特定のオブジェクト・インスタンスがデータベース内に保存されているかをテストするには、2 つの基本的な方法があります。

これらの例では、ID は整数です (Caché が既定で生成する ID)。次の章では、ID がオブジェクトの固有プロパティに基づくようにクラスを定義する方法について説明しています。

ObjectScript によるオブジェクトの存在のテスト

%ExistsId() クラス・メソッドは、指定された ID を確認します。指定されたオブジェクトがデータベース内に存在する場合は True (1) の値を返し、それ以外の場合は False (0) を返します。これは、%PersistentOpens in a new tab から継承されたすべてのクラスで使用できます。以下に例を示します。

 Write ##class(Sample.Person).%ExistsId(1),!   // should be 1 
 Write ##class(Sample.Person).%ExistsId(-1),!  // should be 0

ここでは、最初の行が 1 を返します。これは、Sample.PersonOpens in a new tab%PersistentOpens in a new tab を継承していて、SAMPLES データベースは、このクラスにデータを提供するためです。

%Exists() メソッドも使用できます。このメソッドには、ID ではなく OID が必要になります。

SQL によるオブジェクトの存在のテスト

保存したオブジェクトの存在を SQL でテストするには、指定した ID と一致する %ID フィールドを持つ行を選択する SELECT 文を使用します (保存したオブジェクトの identity プロパティは、%ID フィールドとして投影されます)。

例えば、以下のように埋め込み SQL を使用します。

 &sql(SELECT %ID FROM Sample.Person WHERE %ID = '1')
 Write SQLCODE,!  // should be 0: success

 &sql(SELECT %ID FROM Sample.Person WHERE %ID = '-1')
 Write SQLCODE,!  // should be 100: not found

ここでは、最初のケースは結果として 0 の SQLCODE を返します (成功を意味します)。これは、Sample.PersonOpens in a new tab%PersistentOpens in a new tab を継承していて、SAMPLES データベースは、このクラスにデータを提供するためです。

2 つ目のケースは、文の実行は成功したもののデータは返されないことを示す SQLCODE 100 になります。システムは 0 未満の ID を自動的には生成しないため、このように想定されます。

埋め込み SQL の詳細は、"Caché SQL の使用法" の “埋め込み SQL” を参照してください。SQLCODE の詳細は、"Caché エラー・リファレンス" の “SQLCODE 値とエラー・メッセージ” を参照してください。

保存したオブジェクトのオープン

オブジェクトを開く (ディスクからメモリにオブジェクト・インスタンスをロードする) には、以下のように %OpenId() メソッドを使用します。

classmethod %OpenId(id As %String, 
                    concurrency As %Integer = -1, 
                    ByRef sc As %Status = $$$OK) as %ObjectHandle

以下はその説明です。

  • id は、開くオブジェクトの ID です。

    この例では、ID は整数です。次の章では、ID がオブジェクトの固有プロパティに基づくようにクラスを定義する方法について説明しています。

  • concurrency は、オブジェクトを開くために使用する並行処理レベル (ロック) です。

  • 参照渡しの sc は、%StatusOpens in a new tab の値です。この値は、呼び出しが成功したか、失敗したかを表します。

指定のオブジェクトを開くことができる場合は、このメソッドが OREF を返します。オブジェクトが見つからない場合やオブジェクトが開けない場合は、NULL 値 ("") が返されます。

例 :

 // Open person "10"
 Set person = ##class(Sample.Person).%OpenId(10)

 Write "Person: ",person,!    // should be an object reference

 // Open person "-10"
 Set person = ##class(Sample.Person).%OpenId(-10)

 Write "Person: ",person,!    // should be a null string

Caché Basic では、OpenId コマンドが %OpenId() メソッドと同じ機能をします。

person = OpenId Sample.Person(1)
PrintLn "Name: " & person.Name

%Open() メソッドも使用できます。このメソッドには、ID ではなく OID が必要になります。

%OpenId() に対する複数の呼び出し

同じ ID の Caché プロセス内で %OpenId() が複数回呼び出された場合、メモリに作成されるオブジェクト・インスタンスは 1 つのみです。それ以降の %OpenId() の呼び出しではすべて、既にメモリにロードされているオブジェクトへの参照が返されます。

詳細は、以下の例を参照してください。

' open and modify Person 1 in memory
personA = OpenId Sample.Person(1)
personA.Name = "Black,Jimmy Carl"

' open Person 1 "again"
personB = OpenId Sample.Person(1)

PrintLn "NameA: " & personA.Name
PrintLn "NameB: " & personB.Name

並行処理

%OpenId() メソッドは、入力としてオプションの concurrency 引数を取ります。この引数は、オブジェクト・インスタンスを開くために使用する並行処理レベル (ロックのタイプ) を指定します。

可能なオブジェクト並行処理レベルの詳細は、この章で後述する “オブジェクト同時処理のオプション” を参照してください。

%OpenId() メソッドがオブジェクトのロックを取得できない場合、これは失敗します。

オブジェクトに対する現在の並行処理レベルの設定を上げ下げするには、そのオブジェクトを %OpenId() で再度開いて別の並行処理レベルを指定します。以下はその例です。

 Set person = ##class(Sample.Person).%OpenId(6,0)

並行処理レベル 0 でperson を開き、以下のようにして並行処理レベルを事実上 4 まで引き上げます。

 Set person = ##class(Sample.Person).%OpenId(6,4)

スウィズリング

永続オブジェクトのインスタンスを開いて (メモリにロードして)、そのインスタンスが参照するオブジェクトを使用する場合、この参照されるオブジェクトは自動的に開かれます。このプロセスをスウィズリングといいます。また、“遅延ロード” ということもあります。

例えば、以下のコードは Sample.EmployeeOpens in a new tab オブジェクトのインスタンスを開き、ドット構文を使用して関連する Sample.CompanyOpens in a new tab オブジェクトを自動的にスウィズルします (開きます)。

 // Open employee "101"
 Set emp = ##class(Sample.Employee).%OpenId(101)

 // Automatically open Sample.Company by referring to it:
 Write "Company: ",emp.Company.Name,!

オブジェクトがスウィズルされるときには、アトミック読み取りの既定の並行処理値を使用してオブジェクトが開かれます。

スウィズルされたオブジェクトは、それを参照するオブジェクトや変数がなくなるとすぐに、メモリから削除されます。

ディスクからのオブジェクトの再ロード

データベースに格納された値を使用してメモリ内のオブジェクトを再ロードするには、そのオブジェクトの %Reload() メソッドを呼び出します。

 // Open person "1"
 Set person = ##class(Sample.Person).%OpenId(1)
 Write "Original value: ",person.Name,!

 // modify the object
 Set person.Name = "Black,Jimmy Carl"
 Write "Modified value: ",person.Name,!

 // Now reload the object from disk
 Do person.%Reload()
 Write "Reloaded value: ",person.Name,!

保存された値の読み取り

永続オブジェクトのインスタンスを開き、そのプロパティを変更したと仮定します。また、そのオブジェクトを保存する前に、データベースに保存された元の値を表示するとします。これを行う最も簡単な方法は、SQL 文を使用することです (SQL は、メモリ内のオブジェクトではなく、常にデータベースに対して実行されるものです)。

例 :

 // Open person "1"
 Set person = ##class(Sample.Person).%OpenId(1)
 Write "Original value: ",person.Name,!

 // modify the object
 Set person.Name = "Black,Jimmy Carl"
 Write "Modified value: ",person.Name,!

 // Now see what value is on disk
 Set id = person.%Id()
 &sql(SELECT Name INTO :name
         FROM Sample.Person WHERE %ID = :id)

 Write "Disk value: ",name,!

保存したオブジェクトの削除

永続インタフェースには、データベースからオブジェクトを削除するためのメソッドがあります。

%DeleteId() メソッド

%DeleteId() メソッドは、データベース内に保存されているオブジェクトを削除します。このメソッドは、以下のとおりです。

classmethod %DeleteId(id As %String, concurrency As %Integer = -1) as %Status

以下はその説明です。

  • id は、開くオブジェクトの ID です。

    この例では、ID は整数です。次の章では、ID がオブジェクトの固有プロパティに基づくようにクラスを定義する方法について説明しています。

  • concurrency は、オブジェクト削除時に使用する並行処理レベル (ロック) です。

例 :

 Set sc = ##class(MyApp.MyClass).%DeleteId(id)

%DeleteId() は、オブジェクトが削除されたか否かを示す %StatusOpens in a new tab 値を返します。

オブジェクトが削除される前に、%OnDelete() コールバック・メソッドが存在していれば、%DeleteId() はそれを呼び出します。%OnDelete()%StatusOpens in a new tab 値を返します。%OnDelete() がエラー値を返した場合、オブジェクトは削除されず、現在のトランザクションがロールバックされ、%DeleteId() はエラー値を返します。

%DeleteId() メソッドは、メモリ内のオブジェクト・インスタンスには効果がありません。

%Delete() メソッドも使用できます。このメソッドには、ID ではなく OID が必要になります。

%DeleteExtent() メソッド

%DeleteExtent() メソッドは、エクステント内のすべてのオブジェクト (およびオブジェクトのサブクラス) を削除します。具体的には、エクステント全体を反復処理して、各インスタンスで %DeleteId() メソッドを起動します。

%KillExtent() メソッド

%KillExtent() メソッドは、オブジェクトのエクステントを保存するグローバルを直接削除します (ストリームに関連付けられたデータは含まれません)。%DeleteId() メソッドは呼び出されず、参照整合性アクションも実行されません。このメソッドは、単に開発段階で開発者を支援することを目的としています (従来のリレーショナル・データベース製品にある TRUNCATE TABLE コマンドに類似したものです)。

Caution:

%KillExtent() は、開発環境でのみ使用するためのものであるため、実際のアプリケーションで使用しないでください。%KillExtent() は制約やユーザ実装のコールバックを回避して、データ整合性の問題を引き起こす可能性があります。

オブジェクト識別子のアクセス

オブジェクトが保存されている場合、そのオブジェクトには ID と OID があります。これらは、ディスクで使用する永久識別子です。オブジェクトの OREF があれば、その OREF を使用して、これらの識別子を取得できます。

OREF に関連付けられた ID を調べるには、オブジェクトの %Id() メソッドを呼び出します。以下に例を示します。

 write oref.%Id()

OREF に関連付けられた OID を調べる場合は、以下の 2 つの選択肢があります。

  1. オブジェクトの %Oid() メソッドを呼び出します。以下に例を示します。

     write oref.%Oid()
  2. オブジェクトの %%OID プロパティにアクセスします。このプロパティ名には % 文字が含まれているため、その名前を二重引用符で囲む必要があります。以下に例を示します。

     write oref."%%OID"

オブジェクト同時処理のオプション

オブジェクトを開く場合や削除する場合には、適切に並行処理を指定することが重要です。並行処理はいくつかの異なるレベルで指定できます。

  1. 使用するメソッドの concurrency 引数を指定できます。

    %PersistentOpens in a new tab クラスの多くのメソッドで、この引数 (整数) を指定できます。この引数により、並行処理の制御に使用されるロックの方式が決定されます。後述のサブセクションに、指定可能な値の一覧があります。

    concurrency 引数の指定を省略すると、Caché により、作業対象のクラスの DEFAULTCONCURRENCY クラス・パラメータの値が使用されます。次の項目を参照してください。

  2. 関連するクラスの DEFAULTCONCURRENCY クラス・パラメータを指定できます。すべての永続クラスは、プロセスに既定の並行処理を取得する式としてパラメータを定義する %PersistentOpens in a new tab から、このパラメータを継承します。次の項目を参照してください。

    クラス内で、このパラメータをオーバーライドして、ハードコードされた値を指定することも、独自のルールによって並行処理を決定する式を指定することもできます。どちらの場合も、パラメータの値は、このセクションで後述する指定可能な並行処理の値のいずれかにする必要があります。

  3. 既定の並行処理をプロセスに設定できます。そのためには、$system.OBJ.SetConcurrencyMode() メソッドを使用します (このメソッドは、%SYSTEM.OBJOpens in a new tab クラスの SetConcurrencyMode()Opens in a new tab メソッドです)。

    その他の場合と同じように、指定可能な並行処理の値のいずれかを使用する必要があります。

    $system.OBJ.SetConcurrencyMode() メソッドは、DEFAULTCONCURRENCY クラス・パラメータに明示的な値を指定するクラスには効果がありません。

並行処理を指定する理由

以下のシナリオでは、オブジェクトの読み取り時または書き込み時に、適切に並行処理を制御することが重要になる理由についての例を示しています。以下の例を考えてみます。

  1. プロセス A がオブジェクトを開きます。

    SAMPLES>set o=##class(Sample.Person).%OpenId(5)
     
    SAMPLES>w o
    1@Sample.Person
    
  2. プロセス B がディスクからそのオブジェクトを削除します。

    SAMPLES>set sc=##class(Sample.Person).%DeleteId(5)
     
    SAMPLES>w sc
    1
    
  3. プロセス A が %Save() を使用してオブジェクトを保存し、成功のステータスを受け取ります。

    SAMPLES>set sc=o.%Save()
     
    SAMPLES>w sc
    1
    

ディスク上のデータを確認すると、オブジェクトはディスクに書き込まれていません。

これは、適切な並行処理の制御がない場合の同時処理の例です。例えば、プロセス A がオブジェクトをディスクに保存する可能性がある場合、並行処理 3 または 4 でオブジェクトを開く必要があります (これらの値については、この章で後述します)。この場合、プロセス B はアクセスを拒否される (並行処理違反で失敗する)、またはプロセス A がオブジェクトを解放するまで待機する必要があります。

並行処理の値

このセクションでは、有効な並行処理の値について説明します。まず、以下の詳細に注意してください。

  • アトミック書き込みは、並行処理が 0 より大きいときに保証されます。

  • Caché は、オブジェクトの保存や削除などの処理中にロックの取得と解放を実行します。詳細は、並行処理の値 (どのような制約があるのか、ロック・エスカレーションのステータス、およびストレージ構造) によって異なります。

  • どのような場合でも、オブジェクトがメモリから削除されると、そのオブジェクトのロックも削除されます。

有効な並行処理の値は、以下のとおりです。このリストも示すように、それぞれの値には名前が付いています。

並行処理の値 0 (ロックなし)

ロックを使用しません。

並行処理の値 1 (アトミック読み込み)

ロックは必要に応じて取得および解放され、オブジェクトの読み込みがアトミック処理として実行されることを保証します。

新規オブジェクトの作成時に、Caché がロックを取得することはありません。

オブジェクトを開いている間は、Caché はオブジェクトに対する共有ロックを取得します (アトミック読み込みを保証するために必要な場合)。読み込み処理が完了すると、Caché はロックを解放します。

以下のテーブルに、各シナリオに現れるロックを示します。

  オブジェクトが作成されるとき オブジェクトが開かれている間 オブジェクトが開かれた後 保存処理が完了した後
新規オブジェクト ロックなし N/A N/A ロックなし
既存のオブジェクト N/A 共有ロック (アトミック読み込みを保証するために必要な場合) ロックなし ロックなし
並行処理の値 2 (共有ロック)

1 (アトミック読み込み) と同じですが、オブジェクトを開くときには常に共有ロックを取得します (アトミック読み込みを保証するために必要とされない場合でも、このロックを取得します)。以下のテーブルに、各シナリオに現れるロックを示します。

  オブジェクトが作成されるとき オブジェクトが開かれている間 オブジェクトが開かれた後 保存処理が完了した後
新規オブジェクト ロックなし N/A N/A ロックなし
既存のオブジェクト N/A 共有ロック ロックなし ロックなし
並行処理の値 3 (共有/保持ロック)

新規オブジェクトの作成時に、Caché がロックを取得することはありません。

既存のオブジェクトを開いている間は、Caché はオブジェクトの共有ロックを取得します。

新規オブジェクトの保存後、Caché はオブジェクトの共有ロックを保持します。

以下のテーブルは、シナリオのリストです。

  オブジェクトが作成されるとき オブジェクトが開かれている間 オブジェクトが開かれた後 保存処理が完了した後
新規オブジェクト ロックなし N/A N/A 共有ロック
既存のオブジェクト N/A 共有ロック 共有ロック 共有ロック
並行処理の値 4 (排他/保持ロック)

既存のオブジェクトが開かれるとき、または新規オブジェクトが初めて保存されるときに、Caché は排他ロックを取得します。

以下のテーブルは、シナリオのリストです。

  オブジェクトが作成されるとき オブジェクトが開かれている間 オブジェクトが開かれた後 保存処理が完了した後
新規オブジェクト ロックなし N/A N/A 排他ロック
既存のオブジェクト N/A 排他ロック 排他ロック 排他ロック

並行処理とスウィズルされたオブジェクト

プロパティによって参照されるオブジェクトは、既定の同時処理を使用してアクセスするとスウィズルされます (スウィズルされるオブジェクトが既にメモリ内にある場合は、実際にスウィズルによってオブジェクトが開かれることはありません。それは、既存のメモリ内のオブジェクトを参照するだけであり、その場合、そのオブジェクトの現在の状態が保持され、同時処理は変更されません)。

例えば、1 つのオブジェクト (Obj1) が、別のオブジェクト (Obj2) を参照するプロパティ (Prop1) を持っている場合、Prop1 にアクセスするときに、Obj2 はスウィズルされます。Obj2 は、既に開いていない限り、既定の同時処理を持つことになります。

この既定の動作をオーバーライドするには、以下の 2 つの方法があります。

  • スウィズルされたオブジェクトに対する同時処理を、新しい同時処理を指定する %Open() メソッドの呼び出しでアップグレードします。以下に例を示します。

     Do person.Spouse.%Open(person.Spouse.%Oid(),4,.status) 

    %Open()Opens in a new tab の最初の引数では OID を指定し、2 番目の引数は新しい並行処理を指定し、3 番目の引数 (参照渡し) はメソッドのステータスを受け取ります。

  • オブジェクトをスウィズルする前に、プロセスに対して既定の並行処理を設定します。以下に例を示します。

     Set olddefault = $system.OBJ.SetConcurrencyMode(4) 

    このメソッドは、その引数として新しい同時処理モードを取り、前の同時処理モードを返します。

    異なる並行処理モードが必要なくなった場合は、以下のように既定の並行処理モードをリセットします。

     Do $system.OBJ.SetConcurrencyMode(olddefault)

バージョン確認 (並行処理引数の代替)

オブジェクトを開いたり削除したりするときに、並行処理引数を指定する代わりに、バージョン確認を実装できます。そのためには、VERSIONPROPERTY というクラス・パラメータを指定します。すべての永続クラスに、このパラメータがあります。永続クラスを定義するときに、バージョン確認を有効にする手順は次のとおりです。

  1. 更新可能なバージョン番号を保持した %IntegerOpens in a new tab 型のプロパティを、クラスのインスタンスごとに作成します。

  2. そのプロパティについては、InitialExpression キーワードの値を 0 に設定します。

  3. クラスについては、そのプロパティの名前と等しくなるように、VERSIONPROPERTY クラス・パラメータの値を設定します。VERSIONPROPERTY の値は、サブクラスからは別のプロパティに変更できません。

これでバージョン確認が、クラス内のインスタンスへの更新に組み込まれます。

バージョン確認が実装されると、クラス内のインスタンスがオブジェクトまたは SQL で更新されるたびに VERSIONPROPERTY で指定したプロパティが自動的にインクリメントされます。プロパティがインクリメントされる前に、Caché によってメモリ内の値と保存されている値が比較されます。この値が異なる場合は、同時処理の競合が起こりエラーが表示されます。この値が同じ場合は、プロパティがインクリメントされて保存されます。

Note:

この機能一式を使用して、楽観的同時実行制御を実装できます。

VERSIONPROPERTYInstanceVersion と呼ばれるプロパティを指す場合、クラスの SQL 更新文に同時処理の確認を実装するには、コードを次のように設定することになります。

 SELECT InstanceVersion,Name,SpecialRelevantField,%ID
     FROM my_table
     WHERE %ID = :myid

     // Application performs operations on the selected row

 UPDATE my_table
     SET SpecialRelevantField = :newMoreSpecialValue
     WHERE %ID = :myid AND InstanceVersion = :myversion

ここで、myversion は、元のデータで選択したバージョンのプロパティ値を指します。

FeedbackOpens in a new tab