The Package Wizard

Top  Previous  Next

Package WizardOracle Packageをカプセル化するDelphiクラスを生成するユーティリティを使うのに非常に強力で簡単です。 Package Wizardなしでは、適切なPL/SQLブロックと共にTOracleQueryを定義し、パラメーターに対して変数を定義し、変数値を設定、クエリーを実行、出力パラメーターか関数結果のために変数値を取得しなくてはいけません。TOraclePackageコンポーネントは既にこの処理を簡単にして、一度の呼び出しでプログラムユニットを呼びだし、パラメーターを渡すことができます。しかし、このコンポーネントは簡易化されたインターフェースのため、幾つかの制約があります。

Package Wizardsはこれらパッケージの関数とプロシージャーを内包するOracle Packageselectionのためのクラスを作成します。 もしパラメーターがrecord型なら、これらのrecord型をカプセル化するクラスも生成します。 次のDepartment packageをサンプルとして考えて見ましょう:

パッケージDepartmentの作成か置き換えは

function  Employee_Count(Deptno in dept.deptno%type) return binary_integer;

procedure Select_Record(Dept_Record in out dept%rowtype);

procedure Insert_Record(Dept_Record in dept%rowtype);

procedure Update_Record(Dept_Record in dept%rowtype);

procedure Delete_Record(Deptno in dept.deptno%type);

procedure Get_Description(Deptno in dept.deptno%type, Description out CLOB);

end Department;

 

ボタンの一度押すと、Package Wizardはこのパッケージのための2つのクラスを生成します

type

DeptRowtype = class(TPLSQLRecord)

public

   Deptno: Integer;

   Dname: string;

   Loc: string;

   procedure Assign(Source: TPLSQLRecord); override;

end;

TDepartment = class(TOracleCustomPackage)

public

   function EmployeeCount(Deptno: Integer): Integer;

   procedure SelectRecord(var DeptRecord: DeptRowtype);

   procedure InsertRecord(DeptRecord: DeptRowtype);

   procedure UpdateRecord(DeptRecord: DeptRowtype);

   procedure DeleteRecord(Deptno: Integer);

   procedure GetDescription(Deptno: Integer; out Description: TLOBLocator);

published

   property Name;

   property Session;

   property Cursor;

   property PackageSpecification;

end;

 

DeptRowTypeクラスはプロシージャーに使われているdept%rowtypeパラメーターをカプセル化します。 TDepartmentクラスはDepartment packageをカプセル化し、6つのプログラムユニットの正確な表現を含みます。. これらの格納されたプログラムユニットを呼ぶためには、TDepartment クラスのインスタンスを作成する必要があり、 Sessionプロパティを設定し、そして対応する関数かプロシージャーを呼びます:

Department := TDepartment.Create(nil);

Department.Session := MainSession;

EmpCount := Department.EmployeeCount(10);

Department.Free;

 

見てわかるとおり、TdepartmentクラスはTOracleCustomPackageクラスから由来し、つまりTcomponentの子孫となります。 これは、これらOracle PackageDelphiC++Builder packageにインストールできることを暗に示し、コンポーネントパレットに置くことが出来ます。 これで設計時にTdepartmentコンポーネントをデータモジュールかフォームに置くことができ、TOracleSession コンポーネントにリンクさせることが出来ます。追加的コードなしに実行時に関数やプロシージャーを呼び出すことが出来て、Tdepartmentインスタンスをセットアップ・解放できます:

EmpCount := MainDataModule.Department.EmployeeCount(10);

 

これ以上に簡単にすることは不可能でしょう。Oracle ServerソフトウェアはこれでDelphiC++Builderプログラミング言語の自然な拡張となりました!

利点

Package Wizardを使うことには、TOracleQueryTOraclePackage コンポーネントを使うより多くの利点があります。 いくつかの利点はわかりやすいものですが、それ以外は生成されたパッケージを使うときにわかるかもしれません:

w        パッケージ化されたプログラムユニットを呼ぶためにコンポーネントを作成し維持する必要がありません。 単にPackage Wizardでパッケージを生成し再生するだけです。これは多大な時間の節約になります。
w        DelphiC++Builderのコード完了はパッケージに対しても有効です。 プログラムユニットやパラメーターの名前を忘れたことはありませんか? パラメーターが入力だったか出力だったか忘れたことは? これからは他のDelphiC++Builderクラスの情報を見つけるのと同じぐらい簡単に見つけることが出来るでしょう
w        関数、プロシージャーやパラメーターに対して名前でアクセスすることがないので、コンパイル時に報告される間違いを作ることが難しくなります。 もしコンパイルできるなら、それは実行できます。ToracleQueryToraclePackageのやり方では、実行時にエラーが見つかってしまいます。
w        Package WizardPL/SQL Record型をカプセル化するクラスを生成します。 Record型のパラメーターを持つプログラムユニットは他のプログラムユニット並みに簡単に使うことが出来ます。
w        Package Wizardscalar PL/SQL table型をカプセル化する特別なクラスを使用します。これによりこの型のパラメーターを簡単に使えるようにします。
w        DelphiC++Builder 4以降では、オーバーロードされたプログラムユニットをそのままでみることになります。
w        もしPackage Specificationにあるフォームのドキュメントを入れる場合、 生成されたクラスの中でこの情報を見ることが出来ます。 もしコンポーネントとしてパッケージを使っているなら、read-only PackageSpecification プロパティとしてpackage specificationは見ることが出来ます。
w        パッケージ化された関数とプロシージャーの呼び出しは明示的にcritical sectionをプログラムする必要なく自動的にthread-safeとなります。 これはマルチスレッドからシングルパッケージインスタンスを同時に呼び出すことができることを意味します。
w        パッケージクラスは別々のユニットに生成されるので、 複数のプロジェクトで自動的に再利用することが出来ます。
w        コードを生成して、たった一日で何千行ものコードを作成しましたということを上司に見せることは愉快なことです。

Package Wizardを使う

Package Wizardを開始するために、 DelphiC++Builder IDE'File | New' メニューを選んでください。そして'Oracle Package' アイコンをダブルクリックします。'Oracle'メニューへいき'Package Wizard' アイテムを選んでも構いません。ここで、パッケージ生成の最初の4ステップを示します。

Step 1 パッケージを選ぶ

クラスを作りたいと思うOracle PackageにアクセスできるOracleアカウントを使ってデータベースに接続します。 接続を確立したあと、パッケージの選択リストが現れます。'Show all packages' チェックボックスは、現在のOracleアカウントが持っているパッケージに限るように使われます。

ひとつもしくはそれ以上のパッケージを選択して、'Next'ボタンを押します。

Step 2 - インターフェースの翻訳ルールを定義

パッケージに対する次の翻訳ルールを定義できます:

Always use variants as parameters

文字列、整数、ダブル、そしてTDateTimeパラメーターの代わりに、バリアントパラメーターを生成できます。 パラメーターが常にnullを表すという利点があります。. Integerdoubleパラメーターに関して、nullとゼロの間には何も違いがありません。 このオプションの不利な点は、現在は名前に対してオーバーロード識別子を追加することによって(1, 2,など)で区別されるオーバーロードメソッドが生成できません。 他の不利な点は設計時にパラメーターのデータ型を素早く決定できません。Code Completionは常に'Variant'data型として表します。

Generate overloaded methods

オーバーロードされたメソッドを生成するためにこのオプションを有効にします。 これはDelphiC++Builder 4以降でのみ可能となります。 もしこのオプションが無効だと、オーバーロードされたプログラムユニットは名前にオーバーロード識別子をつけて区別されます。

Case

このオプションはオブジェクト、メソッド、パラメーターの名前の大文字・小文字の使用を制御します。 4つの選択肢があります:

w        Unchanged Oracle Packageで定義された名前のまま使う。 通常これは全て大文字となります。もし"quoted"識別子を使っていなければ!
w        Capitalize それぞれの語を大文字から開始します。そして残りの文字を小文字に。 言葉はアンダースコアで分けられて、INSERT_EMPLOYEEInsert_Employeeになります。 これは初期設定で、もし'Remove underscores'オプションも有効にしていると、通常のOracle命名規則とDelphi / C++Builder命名規則の間で変換します。
w        Uppercase 全ての文字を大文字に。
w        Lowercase 全ての文字を小文字に。

Remove underscores

もしこのオプションが有効になっていると、 Oracle識別子内のアンダースコアが取り除かれます。これは通常'Capitalize' Caseオプションとのコンビネーションで使われます。 このコンビネーションを使うことで、INSERT_EMPLOYEEInsertEmployeeに変換します。

Prefix objects with T

DelphiC++Builderでは、Tで始まるクラス名が慣習となっています。 このオプションを有効にすると、このプリフィックスがOracleパッケージ名に追加されます。 'Capitalize' Caseオプションと共に使うとパッケージDEPARTMENT は結果としてTdepartmentクラスとなります。

Prefix parameters with A

メソッドのパラメーターにAを追加するコーディングスタイルを適用しているかもしれません。 このオプションを有効にしていると、生成されたパラメーター名にこのスタイルを適用します。 例えば、もしメソッドがDEPTNOパラメーターを持っていると、これはAdeptnoに変換されます。 このオプションを使うとパラメーターに対する予約語問題を解消することが出来ます(下記参照)

これらプリファレンスのいくつかは生成されたクラスの名前に影響を与えます。 サンプルはこのページの下にあります。

Step 3 要素を確認し名前を変更する

翻訳ルールを定義したあと、 結果として出来たパッケージ、メソッド、そしてパラメータをみることになります。 この時点で、それらの名前を変更でき、個々のメソッドを除外できます。 もちろん後ほどソースを変更することも出来ます。

もしパッケージがパラメーターとしてのレコードのPL/SQL Tableを持つプログラムユニットを含んでいるなら、するとプログラムユニットは、Direct Oracle Accessから呼び出すことが出来ないので、除外されてしまいます。

名前のいくつかは、予約語の衝突を避けるためにPackage Wizard によって暗に変更されるかもしれません。 識別子名'object', 'type''program' PL/SQLでは完全に正当です。しかし生成クラスに対してコンパイルエラーが発生するでしょう。 このような予約語に出会った場合、'1'が名前に追加されます。

Step 4 - ソースファイルを生成

ソースファイルを生成する前に、次のオプションを設定できます:

Prefix database objects with schema name

もしこのオプションを有効にすると、それぞれのデータベースオブジェクトはスキーマ名でプリフィックスされます。もしデータベースユーザーSCOTTがパッケージDEPARTMENTを所有していると、 結果は次のようになります:

begin

:result := scott.department.employee_count(deptno => :deptno);

end;

 

もしアプリケーションのエンドユーザーのOracleアカウントが(publicprivate)シノニムをこれらのオブジェクトに対して持っていない場合、そしてもしそれぞれのデータベース内でパッケージがSCOTTに所有されているならこれは適切な設定でしょう。 もしこのオプションを無効にすると、次のコードが生成されます:

begin

:result := department.employee_count(deptno => :deptno);

end;

 

全てのユーザーがSCOTTアカウントを使ってデータベースに接続しているか、エンドユーザーのOracleアカウントがSCOTT.DEPARTMENTパッケージに対するシノニムをもっているかどうかを確認しなくてはなりません。

Generate thread safe code

Theread-safeコードを生成するためにこのオプションを有効にします。 この結果、明示的にcritical sectionをプログラムすることなく、マルチスレッドから同時に呼び出されるパッケージクラスとなります。 もちろんこのオプションに関わるとても小さいパフォーマンスのトレードオフがあります。

Include package specification

package specificationのソースは通常プログラムユニットと型に関するなんらかのドキュメントを含んでいます。 このオプションを有効にすることで、package specificationを生成されたソースファイルに含ませることが出来ます。 もしパッケージがコンポーネントとして生成されない場合 (下記参照)、するとソースファイル内にコメントとして含まれます。 もしコンポーネントとして生成されたら、specificationPackageSpecification (read-only)プロパティとして含まれます。もちろん設計時に検査できます。

すぐそばにパッケージに関するドキュメントを持つほかに、 このオプションにはもう一つ利点があります。 クラスが再び生成されたパッケージの正確なイメージを常にもつことになります。 もしパッケージがある程度の頻度で変更される場合、これは有効な情報です。

Generate as components

パッケージコンポーネントに対するレジストレーションコードを生成するためにこのオプションを有効にします。 こうすることで、これらをDelphiC++Builderパッケージに追加し、コンポーネントパレットにも追加します。 パッケージをコンポーネントをとして扱うことは、パッケージインスタンスを明示的に作成し解放しなくてよくなるので、非常に便利になります。そして設計時にそれらをセッションにリンクすることが出来ます。設計時にPackageSpecificationプロパティを見ることができるという利点もあります。

Create component dcr file

それぞれのパッケージクラスに対してデフォルトのアイコン(緑のパッケージシンボル)つきでリソースファイルを生成するためにこのオプションを有効にします。Image Editorを使ってパッケージの機能を視覚的に表すようにアイコンを変更できます。

Component palette

もしパッケージコンポーネントをコンポーネントパレットに追加したいなら、存在するパレットの外にselectionを作るか、新しいパレットの名前を入力できます。 それはDelphiC++Builder パッケージのインストール時に自動的に作成されます。

Path

生成しようとするソースファイルへのパスを入力するか選択します。もしこのフィールドを空白のままにすると、ソースファイルは現在開かれているプロジェクトのディレクトリに置かれます。

Filename

生成しようとしているソースファイルのファイル名を入力します。デフォルトの拡張しは.pasです。

Add to project

生成されたソースファイルを現在のプロジェクトに自動的に追加するためにこのオプションを有効にします。

Open in IDE

自動的に生成されたソースファイルをIDEに開くためにこのオプションを有効にします。

生成されたクラスを使う

それぞれのOracleパッケージは、TOracleCustomPackageから派生したクラス内に実装されます。. このベースクラスは通常使うような機能は以下の2つのプロパティを除いて全く含まれていません:

w        Cursor - 関数かプロシージャー実行中のマウスカーソルの形
w        Session このパッケージインスタンスが使うToracleSessionインスタンス

Tcomponentクラスから由来したToracleCustomPackageクラス子孫です。それゆえそれぞれのパッケージはTcomponentからプロパティとメソッドを継承します。 TDepartmentインスタンスを作るために、セッションにリンクし、関数を呼び、そしてパッケージインスタンスを解放します。 次のコードを使うことが出来ます:

Department := TDepartment.Create(nil);

Department.Session := MainSession;

Department.Cursor := crSQLWait;

EmpCount := Department.EmployeeCount(10);

Department.Free;

 

EmployeeCount関数の実行中は、マウスカーソルの形はSQLの砂時計になります。

パラメーター型

Scalar PL/SQLパラメーター型は、パッケージクラスのstring, integer, doubleそしてTdataTime data型になります。 もし'Always use variants for parameters' オプションを有効にすると、scalarパラメーターは代わりにバリアントで表されます。 複合PL/SQL パラメーター型は特定のクラスで表され、次の章で解説されます。

複合入力か入出力パラメーターに関しては対応するクラスのインスタンスを作る必要があり、メソッドに渡します。 出力パラメーターに関しては、対応するインスタンスはメソッド内部で作成され、この状況ではインスタンスを作る必要はありません。 これはインスタンスがオーバーライトされるので、メモリーリークを引起します。 全ての状況で、アプリケーションは責任をもってインスタンスを解放してください。

Ref Cursor

このパラメーター型はTOracleQuery クラスによって表されます。メソッドを呼んだあと、 列を読み込む前にToracleQueryを実行してください。もしGetEmployeesプロシージャーがdepartmentを持つ全てのemployeeのためにカーソルを返す場合、 このメソッドを呼び出すコードはこのように成ります:

Department := TDepartment.Create(nil);

Department.Session := MainSession;

Department.GetEmployees(10, EmpQuery);

EmpQuery.Execute;

while not EmpQuery.Eof do

begin

ShowMessage(EmpQuery.Field('ename'));

EmpQuery.Next;

end;

EmpQuery.Free;

Department.Free;

 

CLOB, BLOB and BFILE

これら3 LOBデータ型はTLOBLocator クラスによって表されます。 入出力LOBパラメーターに関しては、正しい型のTLOBLocator インスタンスを作ってください(otCLOB, otBLOB, otBFILE)Departmentパッケージが、departmentに対してdescripton CLOBを返すGetDescriptionプロシージャーを持っている場合、このCLOBを取得するコードはこのようになります:

Department := TDepartment.Create(nil);

Department.Session := MainSession;

Department.GetDescription(10, Description);

ShowMessage(Description.AsString);

Description.Free;

Department.Free;

 

オブジェクトとリファレンス

Oracle8オブジェクト型とリファレンスはTOracleObjectTOracleReferenceクラスによって表されます。 入力か入出力オブジェクトとリファレンスに対して、 作成したインスタンス正しい型名を持っているようにしてください。:

Department := TDepartment.Create(nil);

Department.Session := MainSession;

Address := TOracleObject.Create(MainSession, 'TAddress', '');

Address.SetVariable('City', 'New York');

Department.SetAddress(10, Address);

Address.Free;

Department.Free;

 

PL/SQL Tables

Package WizardPL/SQL Tablesscalar data型だけを持ちます。TPLSQLTableクラスはToracleCustomPackageに対してPL/SQL Tableを表します。

Department := TDepartment.Create(nil);

Department.Session := MainSession;

EmpTable := TPLSQLTable.Create(10, 0);

EmpTable.Count := 3;

EmpTable[0] := 7389;

EmpTable[1] := 6711;

EmpTable[2] := 8556;

Department.DeleteEmployees(EmpTable);

EmpTable.Free;

Department.Free;

 

このサンプルでPL/SQL Tableは最高10要素に対して作られ、3つの要素で埋められ、DeleteEmployeesプロシージャーに渡されます。出力パラメーターに対して明示的にPL/SQL Tableを作りません。それでテーブルの最大値を制御できません。 最大文字列サイズはパッケージクラスに記されています。しかし最大テーブルサイズは実装によります。 パッケージクラスのユニットはDefaultPLSQLTableSize変数を含み出力パラメーターのテーブルサイズを定義します。初期値は100です。

: 例えもし'Always use variants for parameters'オプションを無効にしていても、PL/SQL Tableの値は常にバリアントで表されます。

Record types

パッケージ内でパラメーターとして使われるそれぞれのレコード型のために、対応sるうクラスはユニット内で作られます。 このクラスの名前はpackage specificationから派生し、抽象TPLSQLRecord クラスから由来します。 このベースクラスはCreate コンストラクタとAssign メソッドを定義します。もしdept%rowtypeパラメーターを使う場合、必要なコンポーネントを含む対応するDeptRowtypeクラスは宣言されます:

DeptRowtype = class(TPLSQLRecord)

public

Deptno: Integer;

Dname: string;

Loc: string;

procedure Assign(Source: TPLSQLRecord); override;

end;

 

もし'Always use variants for parameters'オプションを有効にしていると、scalarレコードコンポーネントはバリアントで表されます。 複合レコードコンポーネントは次の章で解説されるクラスで表されます。record型のCreateコンストラクタはパラメーターとしてセッションを取ります。ですから、このレコード型インスタンス内にTLOBLocator, ToracleObjectTOracleReferenceインスタンスを自動的に作成します。 SelectRecordプロシージャーを呼ぶために、次のコードを使うことが出来ます:

Department := TDepartment.Create(nil);

Department.Session := MainSession;

DeptRecord := DeptRowtype.Create(MainSession);

DeptRecord.Deptno := 10;

Department.SelectRecord(DeptRecord);

ShowMessage(DeptRecord.Dname);

DeptRecord.Free;

Department.Free;

 

DeptRecord.Freeデストラクタは、Createプロシージャー中、複合レコードコンポーネントのためにインスタンスを破棄します。