InterSystemsCachéでのクラス要求

Andre Derain Landscape ear Chatou

はじめに


クラスクエリ InterSystemsCachéは、COSコードで直接SQLクエリを抽象化するために使用される便利なツールです。 最も単純なケースでは、次のようになります。同じSQLクエリを複数の場所で使用しますが、引数が異なるとします。

毎回書かないようにするために、要求テキストをクラス要求として指定し、将来この名前を名前で参照できます。 また、結果の次の行を自分で取得するためのロジックを記述するカスタムクエリもあります。 カットの下で、すべてを使用する方法についてお話します。


基本クラスクエリ


したがって、基本クラスクエリは、SELECT SQLクエリを表すためのメソッドです。 通常のSQLクエリと同様に、SQLオプティマイザーとコンパイラーによって処理されますが、COSコンテキスト内から呼び出す方が簡単です。 クラス定義では、これらはクエリタイプの要素です(たとえば、メソッドやプロパティに似ています)。 それらは次のように定義されます。


特定の文字で始まる名前を持つすべての人を返すByNameリクエストを含むSample.Personクラスの定義の例
クラスSample.Person Extends%Persistent
{
プロパティ As%String ;
プロパティ DOB As%Date ;
プロパティ SSN As%String ;
クエリ ByName( name As%String = "" As%SQLQuery
ROWSPEC = "ID:%整数、名前:%文字列、DOB:%日付、SSN:%文字列"
CONTAINID = 1 SELECTMODE = "RUNTIME"
COMPILEMODE = "IMMEDIATE" )[ SqlName = SP_Sample_By_Name SqlProc ]
{
SELECT ID Name DOB SSN
FROM サンプル
WHERE 名前 %STARTSWITH :name
名前で 並べ替え
}
}

次のように、COSコンテキストからこの要求を使用できます。

Set ステートメント = ##クラス %SQL.Statement )。 %新規 ()
status = statementを 設定し ます %PrepareClassQuery " Sample.Person " "ByName"
$$$ ISERR ステータス { Do $ system .OBJ DisplayError status }
resultset = statementを 設定し ます。 %実行 "A"
結果セット中 %Next () {
!、 Resultsetを記述し ます。 %Get "名前"
}

または、生成されたqueryNameFuncメソッドを使用して、すぐに結果セットを取得します。

結果セット = ##クラス Sample.Person )を設定します。 ByNameFunc "A"
結果セット中 %Next () {
!、 Resultsetを記述し ます。 %Get "名前"
}


さらに、このクエリは、次の2つの方法でSQLコンテキストから呼び出すことができます。

Call Sample.SP_Sample_By_Name('A') 

 Select * from Sample.SP_Sample_By_Name('A') 

このクラスは、Cachéに付属のSAMPLESエリアにあります。 それはすべて単純なクエリについてです。 それでは、カスタムクエリに移りましょう。

カスタムクラスリクエスト


ほとんどの場合、基本的なクラスクエリで十分です。 ただし、特に、アプリケーションが要求の動作を完全に制御する必要がある場合があります。


それでは、カスタムクラスクエリはどのように記述されますか? queryNameクエリを作成するには、作成から破棄まで、クエリのロジック全体を実装する4つのメソッドを定義します。


次に、これらのメソッドについて詳しく説明します。

QueryNameメソッド


queryNameメソッドは、クエリに関する情報を提供します。


例として、AllRecordsリクエスト(QueryName = AllRecords、メソッドは単にAllRecordsと呼ばれます)を作成します。これは、保存されたクラスのすべてのレコードを返します。

まず、新しいストアドクラスUtils.CustomQueryを作成します。

クラスUtils.CustomQuery Extends %Persistent %Populate
{
プロパティ Prop1 As%String ;
プロパティ Prop2 As%Integer ;
}

次に、AllRecordsリクエストの説明を記述します。

Query AllRecords() As%Query CONTAINID = 1 ROWSPEC = "Id:%String、Prop1:%String、Prop2:%Integer" )[ SqlName = AllRecords SqlProc ]
{
}


QueryNameExecuteメソッド


queryNameExecuteメソッドは、必要なすべてのクエリの初期化を実行します。 次の署名が必要です。

ClassMethod queryNameExecute( ByRef qHandle As%Binary args As%ステータス

どこで:


例に戻りましょう。 範囲を回避するための多くのオプションがあります(カスタムクエリを整理するための主なアプローチについては後述します)。 $ Order関数を使用してグローバルな回避を使用することをお勧めします。 qHandleはそれに応じて現在のID、この場合は空の文字列を格納します。 追加の引数は必要ないため、argは使用されません。 結果は次のとおりです。

ClassMethod AllRecordsExecute( ByRef qHandle As%Binary As%ステータス
{
qHandle = ""に 設定
$$$ OKを終了OK
}

QueryNameFetchメソッド


queryNameFetchメソッドは、 $ Listの形式で単一の結果を返します。 次の署名が必要です。

ClassMethod queryNameFetch( ByRef qHandle As%Binary
ByRef Row As%List
ByRef AtEnd As%Integer = 0 As%Status [ PlaceAfter = queryNameExecute]

どこで:


このメソッド内では、一般に、次の操作が実行されます。

  1. データの終わりに達したかどうかを判別します。
  2. まだデータがある場合:Create%ListおよびRow変数の値を設定します
  3. それ以外の場合は、AtEndを1に設定します
  4. 後続の呼び出しにqHandleを設定します
  5. ステータスを返す

この例では、次のようになります。

ClassMethod AllRecordsFetch( ByRef qHandle As%Binary ByRef Row As%List ByRef AtEnd As%Integer = 0 As%Status
{
#; グローバル化^ Utils.CustomQueryD
#; 次のidをqHandleに書き込み、グローバルの値をvalに新しいidで書き込みます
qHandle = $ Orderに 設定 (^ Utils.CustomQueryD( qHandle )、1、 val
#; データが最後に達したかどうかを確認します
qHandle = ""の 場合 {
AtEnd = 1に 設定
行を 設定 = ""
$$$ OKを終了OK
}
#; そうでない場合、form%List
#; val = $ Lb( ""、Prop1、Prop2)-ストレージ定義を参照
#; 行= $ Lb(Id、Prop1、Prop2)-ROWSPECリクエストAllRecordsを参照
行の 設定 = $ Lb qHandle $ Lg val 、2)、 $ Lg val 、3))
$$$ OKを終了OK
}

QueryNameCloseメソッド


queryNameCloseメソッドは、すべてのデータを受信した後、クエリを終了します。 次の署名が必要です。

ClassMethod queryNameClose( ByRef qHandle As%Binary As%Status [ PlaceAfter = queryNameFetch]

どこで:


この例では、ローカルのqHandle変数を削除する必要があります。

ClassMethod AllRecordsClose( ByRef qHandle As%Binary As%ステータス
{
qhandleを殺す
$$$ OKを終了OK
}

以上です。 クラスをコンパイルした後、AllRecordsクエリは基本クラスクエリと同様に使用できます-using%SQL.Statement。

カスタムクエリロジック


それでは、カスタムクエリロジックをどのように整理できますか? 3つの主なアプローチがあります。


グローバルバイパス


アプローチは、$ Order関数などを使用してグローバルをバイパスすることです。 次の場合に使用する必要があります。


静的SQL


アプローチは、カーソルと静的SQLを使用することです。 これは次の目的で実行できます。


機能:


Utils.CustomQueryの静的SQLの例
Query AllStatic() As%Query CONTAINID = 1 ROWSPEC = "Id:%String、Prop1:%String、Prop2:%Integer" )[ SqlName = AllStatic SqlProc ]
{
}
ClassMethod AllStaticExecute( ByRef qHandle As%Binary As%ステータス
{
&sql( DECLARE C CURSOR FOR
SELECT ID Prop1 Prop2
FROM Utils カスタムクエリ

&sql( OPEN C
$$$ OKを終了OK
}
ClassMethod AllStaticFetch( ByRef qHandle As%Binary ByRef Row As%List ByRef AtEnd As%Integer = 0 As%Status [ PlaceAfter = AllStaticExecute]
{
#; INTOはFETCHを使用する必要があります
&sql( FETCH C INTO :Id 、: Prop1 ,: Prop2
#; データが最後に達したかどうかを確認します
If SQLCODE '= 0) {
AtEnd = 1に 設定
行を 設定 = ""
$$$ OKを終了OK
}
行の 設定 = $ Lb Id Prop1 Prop2
$$$ OKを終了OK
}
ClassMethod AllStaticClose( ByRef qHandle As%Binary As%Status [ PlaceAfter = AllStaticFetch]
{
&sql( CLOSE C
$$$ OKを終了OK
}

動的SQL


アプローチは、他のクラスクエリと動的SQLを使用することです。 SQLの形式で表すことができる実際のクエリに加えて、いくつかの追加アクションを実行する必要がある場合、たとえば、SQLクエリを一度に1つずついくつかの領域で実行する必要がある場合に関連します。 または、リクエストを実行する前に、権利のエスカレーションが必要です。

Utils.CustomQueryの動的SQLの例
Query AllDynamic() As%Query CONTAINID = 1 ROWSPEC = "Id:%String、Prop1:%String、Prop2:%Integer" )[ SqlName = AllDynamic SqlProc ]
{
}
ClassMethod AllDynamicExecute( ByRef qHandle As%Binary As%ステータス
{
qHandle = ## class %SQL.Statement )を設定します。 %ExecDirect (、 "SELECT * FROM Utils.CustomQuery"
$$$ OKを終了OK
}
ClassMethod AllDynamicFetch( ByRef qHandle As%Binary ByRef Row As%List ByRef AtEnd As%Integer = 0 As%ステータス
{
qHandleの 場合 %Next ()= 0 {
AtEnd = 1に 設定
行を 設定 = ""
$$$ OKを終了OK
}
Set Row = $ Lb qHandle %Get "Id" )、 qHandle %Get "Prop1" )、 qHandle %Get "Prop2" ))
$$$ OKを終了OK
}
ClassMethod AllDynamicClose( ByRef qHandle As%Binary As%ステータス
{
qhandleを殺す
$$$ OKを終了OK
}

代替アプローチ-%SQL.CustomResultSet


または、 %SQL.CustomResultSetクラスの子孫としてクエリを定義できます。 ハブに関する%SQL.CustomResultSetの使用に関する記事があります。 このアプローチの利点:


%SQL.CustomResultSetクラスの継承を作成するときは、次の手順を実行する必要があります。

  1. 結果フィールドに一致するプロパティを定義します。
  2. 結果の一部ではなく、リクエストコンテキストを含むプライベートプロパティを定義する
  3. %OpenCursorメソッドをオーバーライドします-コンテキストの初期作成を担当するqueryNameExecuteメソッドに類似しています。 エラーが発生した場合は、%SQLCODEと%Messageを設定します
  4. %Nextメソッドをオーバーライドします-次の結果を取得するqueryNameFetchメソッドに類似しています。 プロパティを入力します。 メソッドは、データがもうない場合は0を返し、データがある場合は1を返します
  5. %CloseCursorメソッドをオーバーライドします-必要に応じて、queryNameCloseメソッドの類似物

Utils.CustomQueryに%SQL.CustomResultSetを使用する例
クラスUtils.CustomQueryRS Extends%SQL.CustomResultSet
{
プロパティ ID As%String ;
プロパティ Prop1 As%String ;
プロパティ Prop2 As%Integer ;
メソッド %OpenCursor() As%Library.Status
{
設定 .. Id = ""
$$$ OKを終了OK
}
メソッド %Next( ByRef sc As%Library.Status As%Library.Integer [ PlaceAfter =%実行]
{
sc = $$$ OKを 設定
設定 .. Id = $ Order (^ Utils.CustomQueryD(.. Id )、1、 val
終了 :.. Id = "" 0
設定 .. Prop1 = $ Lg val 、2)
設定 .. Prop2 = $ Lg val 、3)
$$$ OKを終了OK
}
}

次のようにCOSコードから呼び出すことができます。

結果セット = ##クラス Utils.CustomQueryRS )を設定します。 %新規 ()
結果セット中 %Next () {
resultset を書き込み ます。 Id 、!
}

また、SAMPLES領域には、 Samples.Personクラスのリクエストを実装するSample.CustomResultSetクラスの例があります。

結論


カスタムクエリを使用すると、COSでのSQLコードの抽象化や、SQLだけを実装するのが難しい動作の実装などの問題を解決できます。

参照資料


クラスのリクエスト
グローバルバイパス
静的SQL
動的SQL
%SQL.CustomResultSet
Utils.CustomQueryクラス
Utils.CustomQueryRSクラス

著者は、この記事の執筆に協力してくれたadaptun habrayzerに感謝しています。

Source: https://habr.com/ru/post/J270839/


All Articles