サーバー側プログラミング: PL/SQL、JavaおよびJavaScript

「SQL」では、Structured Query Language(SQL)言語およびデータベースによるSQL文の処理方法について説明します。この章では、データベースに格納された手続き型言語/SQL(PL/SQL)またはJavaプログラムがSQLを使用できる仕組みについて説明します。

サーバー側プログラミングの概要

SQLなどの非手続き型言語では、処理対象のデータ・セットは指定しますが、実行される処理またはその処理の実行方法は指定しません。

手続き型言語プログラムでは、ほとんどの文の実行が、前後の文、およびループまたは条件分岐などの制御構造に依存しますが、このような処理はSQLでは実行できません。手続き型言語と非手続き型言語の相違を例示するために、次のSQL文でemployees表に問い合せるとします。

SELECT employee_id, department_id, last_name, salary FROM employees;

この文はデータを要求しますが、データにロジックを適用しません。ただし、データ・セットの各従業員が給与および部門の業績に基づく昇給の対象者になるかどうかを判定するアプリケーションが必要であるとします。昇給の必要条件は、従業員が過去5年間に3回を超える昇給を受けなかったことです。昇給する場合は、アプリケーションで給与を調整してマネージャに電子メールを送信し、昇給しない場合は、アプリケーションでレポートを更新する必要があります。

この処理を実行するために、条件付きロジックおよびプログラム・フロー制御を必要とする手続き型データベース・アプリケーションでSQLを使用できるようにします。基本的な開発アプローチは次のとおりです。

この章では、2番目のアプローチについて説明します。サーバー側プログラミングの主な利点は、データベースに組み込まれた機能を任意の場所にデプロイできることです。指定されたオペレーティング・システム上でタスクを実行するための最善の方法を決定するのは、アプリケーションではなくデータベースです。また、サブプログラムを使用すると、サーバー上にアプリケーション処理を集中化することでスケーラビリティを向上させることができ、また、クライアントでコードを再利用することもできます。サブプログラム・コールは迅速かつ効率的であるため、1回のコールで計算集中型のストアド・サブプログラムを開始し、ネットワーク・トラフィックを軽減できます。

Oracle AI Databaseにデータ・ロジックを格納するには、次の言語を使用できます。

関連情報:

PL/SQLの概要

PL/SQLは、使用しやすく、SQLとシームレスで、堅牢かつ移植性を備えたセキュアなサーバー側に格納される手続き型言語を提供します。PL/SQLユニットというプロシージャ・オブジェクトを使用してデータベース・データにアクセスし、データを処理できます。

PL/SQLユニットは、一般に、次のように分類されます。

PL/SQLのコンパイラとインタプリタはOracle SQL Developerに埋め込まれており、開発者にクライアントとサーバーの両方で一貫性のある、高度な開発モデルを提供しています。また、PL/SQLストアド・プロシージャは、Pro*C、JDBC、ODBCまたはOCIなどの複数のデータベース・クライアント、およびOracle ReportsとOracle Formsからコールできます。

関連情報:

PL/SQLサブプログラム

PL/SQLサブプログラムは、コール元から、入力のみ、出力のみまたは入出力の値が入るパラメータを指定できる名前付きのPL/SQLブロックです。

サブプログラムは特定の問題を解決するか、関連するタスクを実行してモジュール式のメンテナンス可能なデータベース・アプリケーションのビルディング・ブロックとして機能します。サブプログラムが、PL/SQLプロシージャまたはPL/SQLファンクションのいずれかであること。プロシージャとファンクションは常にコール元に対して1つの値を戻すが、プロシージャでは戻さない点を除き、プロシージャとファンクションは同じです。この章では、この章のPL/SQLプロシージャという用語は、プロシージャまたはファンクションのいずれかを指します。

関連情報:

PL/SQLサブプログラムの利点

サーバー側プログラミングにはクライアント側プログラミングよりも多くの利点があります。

次のような利点があります。

関連情報:

PL/SQLサブプログラムの作成

スタンドアロンのストアド・サブプログラムは、CREATE PROCEDURE文またはCREATE FUNCTION文を使用してスキーマ・レベルで作成されたサブプログラムです。パッケージに定義されているサブプログラムがパッケージ・サブプログラムと呼ばれ、パッケージの一部とみなされます。

データベースは、データ・ディクショナリにスキーマ・オブジェクトとしてサブプログラムを格納します。サブプログラムには、パラメータの記述などの仕様部と本体があります。

例11-1 PL/SQLプロシージャ

この例に、スタンドアロンのPL/SQLプロシージャhire_employeesを作成する文の一部を示します。プロシージャは、employees表に1つの行を挿入します。

CREATE PROCEDURE hire_employees
  (p_last_name VARCHAR2, p_job_id VARCHAR2, p_manager_id NUMBER,
   p_hire_date DATE, p_salary NUMBER, p_commission_pct NUMBER,
   p_department_id NUMBER)
IS
BEGIN
.
.
.
  INSERT INTO employees (employee_id, last_name, job_id, manager_id, hire_date,
    salary, commission_pct, department_id)
  VALUES (emp_sequence.NEXTVAL, p_last_name, p_job_id, p_manager_id,
          p_hire_date, p_salary, p_commission_pct, p_department_id);
.
.
.
END;

関連情報:

PL/SQLサブプログラムの実行

ユーザーは、複数の方法で対話的にサブプログラムを実行できます。

オプションは:

次の図に、各種のデータベース・アプリケーションがhire_employeesをコールする状態を示します。

図1: ストアド・プロシージャの複数の実行

画像cncpt073.gifの説明

また、権限を付与されたユーザーは、Oracle Enterprise ManagerまたはSQL*Plusを使用して、次のような文でhire_employeesプロシージャを実行できます。

EXECUTE hire_employees ('TSMITH', 'CLERK', 1037, SYSDATE, 500, NULL, 20);

この文により、employees表にTSMITHのための新しいレコードが挿入されます。

ストアド・プロシージャは、プロシージャの本体で参照されるオブジェクトに依存しています。データベースは、この依存性を自動的に追跡して管理します。たとえば、プロシージャに影響を及ぼす方法でhire_employeesプロシージャが参照しているemployees表の定義を変更した場合は、プロシージャを再コンパイルして、引き続き設計どおりに機能することを確認する必要があります。通常、データベースはこのような依存性の管理を自動的に処理します。

関連情報:

PL/SQLパッケージ

PL/SQLパッケージとは、関連するサブプログラムとそれらのサブプログラムが使用するカーソルと変数をグループとして使用したもので、1単位として継続的に使用できるようにデータベースに格納しています。パッケージ化されたプログラムは、アプリケーションまたはユーザーから明示的にコールできます。

Oracle AI Databaseには、データベースの機能を拡張し、SQL機能へのPL/SQLアクセスを提供する、提供されている多くのパッケージが含まれています。たとえば、UTL_HTTPパッケージを使用すると、PL/SQLおよびSQLからのHTTPコールアウトでインターネット上のデータにアクセスしたり、Oracle Web Server Cartridgesをコールできます。オラクル社が提供するパッケージは、アプリケーションの作成時や、独自のストアド・プロシージャを作成する場合に使用できます。

PL/SQLパッケージの利点

PL/SQLパッケージは、アプリケーション開発者に多くの利点を提供します。

次のような利点があります。

PL/SQLパッケージの作成

パッケージは、仕様部と本体の2つの部分に分けて作成します。パッケージの仕様部ではパッケージのすべてのパブリックな構成メンバーを宣言し、パッケージの本体ではパッケージのすべての構成メンバー(パブリックとプライベート)を定義します。

次の例は、employees_managementのパッケージ仕様を作成する文の一部で、このパッケージは、従業員データベースの管理に使用される複数のサブプログラムをカプセル化します。パッケージの各部分は、異なる文を使用して作成されます。

CREATE PACKAGE employees_management AS
  FUNCTION hire_employees (last_name VARCHAR2, job_id VARCHAR2, manager_id NUMBER,
    salary NUMBER, commission_pct NUMBER, department_id NUMBER) RETURN NUMBER;
  PROCEDURE fire_employees(employee_id NUMBER);
  PROCEDURE salary_raise(employee_id NUMBER, salary_incr NUMBER);
.
.
.
  no_sal EXCEPTION;
END employees_management;

仕様部に、ファンクションhire_employees、プロシージャfire_employeesおよびsalary_raiseと、例外であるno_salが宣言されています。パッケージへのアクセス権を持つユーザーは、これらすべてのパブリック・プログラム・オブジェクトを使用できます。

CREATE PACKAGE BODY文では、仕様部で宣言されたオブジェクトを定義します。パッケージ本体は、パッケージと同じスキーマに作成する必要があります。パッケージの作成後は、これらのうちいずれかのパブリック・プロシージャまたはファンクションをコールするか、パッケージのいずれかのパブリック例外を呼び出すアプリケーションを開発できます。

CREATE PACKAGE文について学習するには、『Oracle AI Database PL/SQL言語リファレンス』も参照してください。

PL/SQLパッケージ・サブプログラムの実行

データベース・トリガー、ストアド・サブプログラム、3GLアプリケーション・プログラムおよびOracleツールは、パッケージの内容を参照できます。

次の図に、employees_managementパッケージ内のプロシージャおよびファンクションを呼び出すデータベース・アプリケーションを示します。

図2: PL/SQLパッケージ内のサブプログラムのコール

画像cncpt075.gifの説明

データベース・アプリケーションは、必要に応じて明示的にパッケージ・プロシージャをコールします。employees_managementパッケージに対する権限を付与されたユーザーは、そこに含まれている任意のプロシージャを明示的に実行できます。たとえば、SQL*Plusでは、次の文を発行してhire_employeesパッケージ・プロシージャを実行できます。

EXECUTE employees_management.hire_employees ('TSMITH', 'CLERK', 1037, SYSDATE, 500, NULL, 20);

関連情報:

PL/SQLの無名ブロック

PL/SQL無名ブロックは名前が付与されていない非永続PL/SQLユニットです。

典型的な無名ブロックの用途は次のとおりです。

無名ブロックには、ストアド・サブプログラムのコードの再利用という利点はありません。表11-1に、2つのPL/SQLユニット・タイプの相違点の概要を示します。

表1無名ブロックとサブプログラムの相違点

PL/SQLユニット… 匿名ブロック サブプログラム
名前で指定されますか。 いいえ はい
再利用ごとにコンパイルするか いいえ いいえ
データベースに格納されているか。 いいえ はい
他のアプリケーションから起動できるか いいえ はい
バインド変数値を戻すことができるか はい はい
関数値を戻すことができるか いいえ はい
パラメータを受け取ることができるか いいえ はい

無名ブロックは、オプションの宣言部、実行可能部分および1つまたは複数のオプションの例外ハンドラで構成されます。次のサンプルの無名ブロックでは、変数に格納する従業員の姓を選択して、その名前を出力しています。

DECLARE
  v_lname VARCHAR2(25);
BEGIN
  SELECT last_name
    INTO v_lname
  FROM employees
  WHERE employee_id = 101;
  DBMS_OUTPUT.PUT_LINE('Employee last name is '||v_lname);
END;

Oracle AI Databaseでは、PL/SQLブロックがコンパイルされ、SGAの共有プールに配置されますが、現行インスタンス以外で再利用するためにソース・コードおよびコンパイル済バージョンのデータベースをデータベースに格納することはありません。トリガーとは異なり、無名ブロックはメモリーにロードされるたびにコンパイルされます。共有SQLでは、共有プールに入っている無名のPL/SQLブロックがその共有プールからフラッシュされるまでの間に、その共有プールを再利用および共有することができます。

無名PL/SQLブロックについてさらに学習するには、『Oracle AI Database開発ガイド』も参照してください。

PL/SQLの言語構造

PL/SQLブロックには、様々な異なるPL/SQL言語構造を組み込むことができます。

これらの構造には、次のものが含まれます。

PL/SQLでは、完全なテキストが実行時まで認識されない動的SQL文を実行できます。動的SQL文は、実行時にプログラムに入力されたり、プログラムにより作成される文字列に格納されます。この技法により、汎用プロシージャを作成できます。たとえば、実行時までは名前がわからない表を操作するプロシージャを作成できます。

関連情報:

PL/SQLのコレクションとレコード

多数のプログラミング技法は、配列、構造体、リスト、ネストした表、セットおよびツリーなどのコレクション型を使用します。

データベース・アプリケーションで収集技法をサポートするために、PL/SQLにはTABLEおよびVARRAYのデータ型が用意されています。これらの型を使用すると、連想配列、ネストした表および可変サイズ配列を宣言できます。

回収

PL/SQLコレクションは、すべて同じ型の要素の順序付きグループです。

各要素には、コレクション内での位置を決定する一意の添字が付いています。コレクションを作成するには、コレクション型を最初に定義してから、その型の変数を宣言します。

コレクションの機能は、ほとんどの第3世代プログラミング言語に見られる配列と同じです。また、コレクションはパラメータとして渡すことができます。そのため、データベース表との間や、クライアント側アプリケーションとストアド・サブプログラムの間で、データの列を移動するときに使用できます。

レコード

PL/SQLレコードは、様々な型のデータ値を格納できるコンポジット変数で、C、C++またはJavaの0型に似ています。レコードは、表内の行データまたは表内の行の特定列を保持する場合に役立ちます。

名前、給与および採用日など、従業員に関するデータがあるとします。これらの項目は、型は異なりますが論理的には関連しています。項目ごとに1フィールドを含むレコードを使用すると、データを論理単位として処理できます。

%ROWTYPE属性を使用すると、表の1行やカーソルからフェッチされる1行を表すレコードを宣言できます。ユーザー定義レコードの場合は、所有しているフィールドを宣言できます。

関連項目:PL/ SQLレコードの使用方法を学習するには、『Oracle AI DatabasePL/ SQL言語リファレンス』

PL/SQLの実行方法

PL/SQLは解析済の実行およびシステム固有の実行の両方をサポートします。

解析済の実行では、PL/SQLソース・コードがいわゆるバイトコード表現にコンパイルされます。Oracle AI Databaseの一部として実装されている移植可能仮想コンピュータによって、このバイトコードが実行されます。

ネイティブ実行では、計算集中型のユニットで最大限のパフォーマンスが発揮されます。この場合は、PL/SQLユニットのソース・コードが特定のプラットフォームのオブジェクト・コードに直接コンパイルされます。このオブジェクト・コードは、Oracle AI Databaseにリンクされます。

PL/SQLエンジンは、PL/SQLユニットを定義、コンパイルおよび実行します。このエンジンは、Oracle AI Databaseを含む多数のOracle製品に組み込まれた特別なコンポーネントです。多くのOracle製品にPL/SQLコンポーネントが含まれていますが、このトピックでは、Oracle AI Databaseに格納でき、Oracle AI DatabaseのPL/SQLエンジンを使用して処理できるPL/SQLユニットについて説明します。各Oracleツール用のドキュメントでは、それぞれのPL/SQL機能について説明します。

次の図に、Oracle AI Databaseに含まれているPL/SQLエンジンを示します。

図3: PL/SQLエンジンとOracle AI Database

画像cncpt070.gifの説明

PL/SQLユニットは、データベースに格納されています。アプリケーションによってストアド・プロシージャをコールすると、データベースによって、コンパイルされたPL/SQLユニットがシステム・グローバル領域(SGA)内の共有プールにロードされます。PL/SQL文エグゼキュータとSQL文エグゼキュータは、連動してプロシージャ内の文を処理します。

別のPL/SQLブロック(無名ブロックまたは別のストアド・プロシージャ)からストアド・プロシージャをコールすることもできます。たとえば、Oracle Formsからストアド・プロシージャをコールできます。

Oracle AI Database上で実行されるPL/SQLプロシージャは、Cプログラミング言語で記述され、共有ライブラリに格納された外部プロシージャまたはファンクションをコールできます。Cルーチンは、Oracle AI Databaseとは別のアドレス空内で実行されます。

関連情報:

Oracle AI DatabaseにおけるJavaの概要

Javaは、優れたオブジェクト指向プログラミング言語として登場しました。

Javaの機能は、次のとおりです。

データベースは、複雑な問合せおよび複数のデータ・ビューをサポートする動的データ処理エンジンを備えたJavaプログラムを提供しています。クライアント要求は、即時処理されるようにデータ問合せとして組み立てられます。問合せの結果は動的に生成されます。

JavaとOracle AI Databaseの組合せによって、ビジネス・ニーズの変化に従って簡単に更新できるコンポーネントベースでネットワーク中心のアプリケーションを作成できます。また、アプリケーションおよびデータ・ストアを、デスクトップ上およびインテリジェント・ネットワークやネットワーク中心のサーバーに移動できます。さらに重要なのは、任意のクライアント・デバイスからこれらのアプリケーションおよびデータ・ストアにアクセスできることです。

次の図は、従来の2層のクライアント/サーバー構成を示しています。この構成では、クライアントは、PL/SQLサブプログラムをコールする際と同じ方法でJavaストアド・プロシージャをコールします。

図4: 2層のクライアント/サーバー構成

画像cncpt236.gifの説明

Java仮想マシン(JVM)の概要

JVMはコンパイル済のJavaコードを実行する仮想プロセッサである。

Javaソース・コードは、プラットフォームに依存しない低レベルのマシン命令(バイト・コードと呼ばれる)にコンパイルされます。Javaバイトコードは、JVMを介して、プラットフォームに依存する処理に解釈されます。

Oracle JVMの概要

Oracle JVMは、Pure Javaアプリケーションを実行する標準的なJava互換環境である。JLSおよびJVMの仕様と互換性があります。

Oracle JVMでは、標準のJavaバイナリ形式とAPIがサポートされます。また、Oracle AI Databaseは、実行時の動的クラス・ロードなど、標準のJava言語セマンティクスに準拠します。

次の図に、OracleのJavaアプリケーションがJavaコア・クラス・ライブラリの最上位に配置され、さらに、Oracle JVMの最上位に配置される様子を示します。OracleのJavaサポート・システムはデータベース内に配置されるので、JVMはオペレーティング・システムと直接対話するかわりに、データベースのライブラリと対話します。

図5: Javaコンポーネントの構造

画像cncpt206.gifの説明

他のJava環境とは異なり、Oracle JVMはOracle AI Database内に埋め込まれています。Oracle JVMと典型的なクライアントJVMには、いくつかの重要な違いがあります。たとえば、標準のJava環境では、コマンドラインで次のコマンドを発行し、インタプリタを介してJavaアプリケーションを実行します(classnameは、JVMにより最初に解釈されるクラス名です)。

java classname

このコマンドによって、オペレーティング・システム上のプロセス内でアプリケーションが実行されます。ただし、コマンドライン・インタフェースを使用していない場合は、アプリケーションをシステムにロードして、インタフェースをパブリッシュしてから、データベース・データ・ディクショナリ内でアプリケーションを実行する必要があります

参照: Oracle JVMと典型的なクライアントJVMのその他の相違の詳細は、『Oracle AI Database Java開発者ガイド』を参照してください。

Oracle JVMの主要コンポーネント

Oracle JVMはデータベースのメモリー・ヒープを共有してリレーショナル・データに直接アクセスするため、データベース・カーネルと同じ処理領域およびアドレス領域で実行されます。この設計によりメモリーの使用を最適化して、スループットを向上させます。

Oracle JVMは、Javaオブジェクトの実行時環境を提供します。Javaのデータ構造、メソッド・ディスパッチ、例外処理および言語レベル・スレッドを完全にサポートします。また、java.lang、java.io、java.net、java.mathおよびjava.utilなど、すべてのコアJavaクラス・ライブラリもサポートしています。

次の図に、Oracle JVMの主要なコンポーネントを示します。

図6: Oracle JVMの主要コンポーネント

画像cncpt254.gifの説明

Oracle JVMでは、標準のJavaネームスペースがデータベース・スキーマに埋め込まれます。この機能により、JavaプログラムはOracle AI Databaseに格納されたJavaオブジェクトおよび企業内のアプリケーション・サーバーにアクセスできます。

また、Oracle JVMは、データベースのスケーラブルな共有メモリー・アーキテクチャと緊密に統合されています。Javaプログラムでは、ユーザーの介入なしで、コール、セッションおよびオブジェクトの存続期間を効率的に使用します。結果として、セッションが継続状態であっても、Oracle JVMおよび中間層のJavaビジネス・オブジェクトのスケーラビリティを維持できます。

関連項目: Oracle JVMの主要なコンポーネントの詳細は、Oracle AI Database Java開発者ガイドに関する項を参照してください。

Javaプログラミング環境

Oracleは、Javaアプリケーションを作成、デプロイおよび管理するためのエンドツーエンドJavaソリューションをエンタープライズ・アプリケーション開発者に提供します。

このソリューションは、クライアント側とサーバー側のプログラム・インタフェース、Java開発をサポートするツール、およびOracle AI Databaseと統合されたJava Virtual Machineで構成されます。これらの製品はいずれも、Java標準との互換性を持っています。

Javaプログラミング環境は、次の追加機能で構成されています。

Javaストアド・手順

Javaストアド・プロシージャとは、SQLにパブリッシュされデータベースに格納されるJavaメソッドのことです。

PL/SQLサブプログラムと同様に、Javaプロシージャには、SQL*Plusなどの製品で直接起動する方法と、トリガーを使用して間接的に起動する方法があります。また、Oracle NetクライアントOCI、プリコンパイラまたはJDBCからアクセスできます。

Javaメソッドをパブリッシュするには、Javaメソッド名、パラメータ・タイプおよび戻り型を対応するSQLにマップするコール仕様を記述します。クライアント・アプリケーションによってコールされた場合、Javaストアド・プロシージャでは、引数を受け取り、Javaクラスを参照してJava結果値を返すことができます。

アプリケーションは、コール仕様の名前を参照することにより、Javaメソッドをコールします。ランタイム・システムは、Oracleデータ・ディクショナリでコール仕様の定義を検索し、該当のJavaメソッドを実行します。

また、Javaを使用して、PL/SQLに依存しない強力なプログラムを開発することもできます。Oracle AI Databaseには、Javaプログラミング言語とJVMの完全準拠の実装が用意されています。

関連項目: 『Oracle AI Database Java開発者ガイド』では、Javaでストアド・プロシージャを記述する方法と、PL/SQLからストアド・プロシージャにアクセスする方法、およびJavaからPL/SQL機能にアクセスする方法について説明しています。

JavaおよびPL/SQLの統合

既存のPL/SQLプログラムをJavaからコールしたり、JavaプログラムをPL/SQLからコールできます。このソリューションでは、PL/SQLおよびJavaコードを保護して活用します。

Oracle AI Databaseでは、JavaからSQLデータにアクセスするためのJDBCがクライアントとサーバーの両方に用意されています。

JDBCドライバ

JDBCは、データベースがデータベースに接続してSQL文を実行し、データベースへの問合せを実行できるようにするデータベース・アクセス・プロトコルです。

コアJavaクラス・ライブラリには、JDBC APIのjava.sqlが1つのみ用意されています。ただし、JDBCは、ベンダーが特定のデータベースを必要に応じて特化するためのドライバを提供できるように設計されています。Oracleには、次の表に示す個々のJDBCドライバが用意されています。

表2 JDBCドライバ

ドライバ 摘要
JDBC Thinドライバ JDBC Thinドライバを使用すると、Oracle SQLのデータにアクセスする純粋なJavaアプリケーションおよびアプレットを作成できます。JDBC Thinドライバは、他のJavaアプレットと同様にWebページから動的にダウンロードできるため、Webベースのアプリケーションとアプレットに特に適しています。
JDBC OCIドライバ JDBC OCIドライバは、クライアントまたは中間層にあるOracle固有のネイティブ・コード(つまり非Javaコード)およびライブラリにアクセスし、JDBC Thinドライバに比べてパフォーマンスが向上しますが、サイズが極端に大きくなることと、クライアント側でのインストールというコストの問題があります。
サーバー側JDBC内部ドライバ Oracle AI Databaseは、サーバー上でJavaコードが実行される場合に、サーバー側内部ドライバを使用します。これにより、サーバーのOracle JVM内で実行中のJavaアプリケーションは、JDBCを使用してローカルに定義されたデータ(つまり、同じシステムの同じプロセスにあるデータ)にアクセスできます。また、基礎となるOracle RDBMSライブラリを直接使用できるため、パフォーマンスが向上し、JavaコードとSQLデータの間にネットワーク接続が介入することによるオーバーヘッドは発生しません。Oracle AI Databaseでは、サーバー上で同じJava-SQLインタフェースがサポートされるため、デプロイ時にコードの再操作を必要としません。

関連情報:

Oracle AI DatabaseにおけるJavaScriptの概要

JavaScriptは、現在最も一般的なプログラミング言語の1つです。Linux x86-64またはLinux aarch64にデプロイされたOracle AI Databaseは、サーバー側開発でサポートされている言語のリストにJavaScriptを追加します。

JavaScriptのモジュールをデータベースに格納し、存続するデータを処理することで、クライアント側開発アプローチより大幅に改善される可能性があります。JavaScriptは、マルチリンガル・エンジン(MLE)によって実行されます。

開発者は、スタンドアロンのJavaScriptコードを実行するか、JavaScriptコードをデータベースにモジュールとしてネイティブに格納するかを選択できます。

データベースへのビジネス・ロジックのモジュールとしての格納

クライアント側開発関連のJavaScriptと同様に、コードをモジュールの形式で一緒に格納できます。

PL/SQLバックグラウンドを持つ開発者は、JavaScriptモジュールはPL/SQLパッケージ本体と同等のものであると考えることができますが、JavaScriptで記述されています。モジュールで宣言されたファンクションは、エクスポートするために、またはプライベートを維持するためにマークできます。

Oracle AI DatabaseのJavaScriptは、ECMAScript 2023標準に準拠しており、データベース内にビジネス・ロジックを格納する様々な機能を開発ユーザーに提供します。

注意:

関連トピック

MLEを使用してJavaScriptモジュールを作成する例を次に示します。

create mle module if not exists example_module
language javascript as

export function string2obj(inputString) {
   if ( inputString === undefined ) {
      throw `must provide a string in the form of key1=value1;...;keyN=valueN`;
   }

   let myObject = {};
   if ( inputString.length === 0 ) {
      return myObject;
   }

   const kvPairs = inputString.split(";");
   kvPairs.forEach( pair => {
      const tuple = pair.split("=");
      if ( tuple.length === 1 ) {
          tuple[1] = false;
      } else if ( tuple.length != 2 ) {
          throw "parse error: you need to use exactly one '=' between key and value and not use '=' in either key or value";
      }
      myObject[tuple[0]] = tuple[1];
  });

  return myObject;
}
/

MLEモジュールとも呼ばれるJavaScriptモジュールは、ユーザーのスキーマに格納されています。この唯一のファンクションstring2obj()は、コール仕様を作成してSQLおよびPL/SQLに公開できます。コール仕様は、SQLおよびPL/SQLをコールできるすべての場所でJavaScriptコードをコールできるPL/SQLプログラム・ユニットです。string2obj()のコール仕様は、次のように記述できます。

create function if not exists p_string_to_JSON(p_str varchar2) return JSON
as mle module example_module
   signature 'string2obj(string)';
/

ファンクションが作成されると、SQLからコールできます。

declare
   l_json   JSON;
   l_string VARCHAR2(100);
begin
   l_string := 'a=1;b=2;c=3;d';
   l_json := p_string_to_JSON(l_string);
   dbms_output.put_line(json_serialize(l_json PRETTY));
end;
/

{
  "a" : "1",
  "b" : "2",
  "c" : "3",
  "d" : false
}

PL/SQL procedure successfully completed.

JavaScriptコードの動的実行

スタンドアロンJavaScriptコードを実行するフレームワーク開発者およびユーザーは、データベースにコードを格納する必要なく、DBMS_MLEパッケージとそのサブルーチンを使用できます。

たとえば、Oracle Application Expressおよびデータベース・アクションでは、DBMS_MLEを統合し、PL/SQLパッケージが起動されるという事実を隠します。

MLEモジュールと比較して、DBMS_MLEを使用すると、アプリケーション全体の記述がより難しくなります。たとえば、アプリケーションを個別のエンティティに論理的に分割することがより難しくなります。実行後デバッグ機能にはモジュールが必要で、コード・スニペットの問題のトラブルシューティングが難しくなります。継続的統合(CI)パイプラインでは、同じファイル内のPL/SQLコードとJavaScriptコードのlintに苦労する可能性があります。同様に、統合開発環境(IDE)でも問題が生じる可能性があります。ほとんどの環境では、PL/SQLとJavaScriptの両方を同じウィンドウで扱うことが困難であるためです。

注意:

関連トピック

インラインJavaScriptストアド・プロシージャ

インラインMLEコール仕様では、CREATE FUNCTIONおよびCREATE PROCEDUREのDDLにJavaScriptコードが直接埋め込まれます。

JavaScriptを使用して単純な機能を迅速に実装する場合は、インラインMLEコール仕様が適切な選択である可能性があります。このオプションでは、JavaScriptコードを含む別のモジュールをデプロイする必要はありません。JavaScriptファンクションはコール仕様自体の定義に組み込まれています。

MLE LANGUAGE句は、JavaScriptを使用してファンクションが実装されるように指定する際に使用します。言語名に続く文字列は、コール仕様の機能を実装するJavaScriptファンクションの本体として扱われます。コードが実行されると、PL/SQLパラメータはデフォルトのJavaScript型に自動的に変換され、同じ名前のパラメータとしてJavaScriptファンクションに渡されます。引用符で囲まれていないパラメータ名は、すべて大文字のJavaScript名にマップされます。JavaScriptファンクションにより戻される値は、MLEモジュールのコール指定と同様に、PL/SQLコール仕様の戻り型に変換されます。

注意:

関連トピック

トリガーの概要

データベース・トリガーはコンパイル済のストアド・プログラム・ユニットで、PL/SQL、JavaまたはJavaScriptのいずれかで記述され、特定の状況でOracle Databaseによって自動的に呼出し(起動)が行われます。

トリガーは、次のいずれかの操作が発生するたびに起動します。

  1. 特定の表またはビューで、ユーザーによってDML文が発行されたとき。

    DML文は、スキーマ・オブジェクトのデータを変更します。たとえば、行の挿入および削除はDMLの処理です。

  2. 特定のユーザーまたは任意のユーザーが発行したDDL文

    DDL文は、スキーマ・オブジェクトを定義します。たとえば、表の作成および列の追加はDDLの処理です。

  3. データベースイベント

    ユーザーのログインまたはログオフ、エラーおよびデータベースの起動または停止は、トリガーを起動できるイベントです。

トリガーはサブプログラムに類似したスキーマ・オブジェクトですが、起動方法が異なります。サブプログラムはユーザー、アプリケーションまたはトリガーによって明示的に実行されます。トリガーは、トリガー・イベントが発生したときにデータベースによって暗黙的に起動されます。

関連情報:

トリガーの利点

トリガーを正しく使用すると、ユーザーはデータベースをより効率的に使用するさらに堅牢なアプリケーションを構築してデプロイできます。

トリガーは、次のような目的で使用できます。

トリガーを使用して、すべてのクライアント・アプリケーションに共通の低レベルでのビジネス・ルールを強制実行できます。たとえば、複数のアプリケーションからemployees表にアクセスできます。この表でのトリガーで挿入されるデータの形式が保証される場合は、クライアントごとにこのビジネス・ロジックを再作成する必要はありません。アプリケーションはトリガーを回避できないため、トリガー内のビジネス・ロジックが自動的に使用されます。

トリガーと整合性制約を併用すると、任意の整合性規則を定義して規定できます。ただし、Oracleでは、整合性制約を使用して定義できない複雑なビジネス・ルールを適用するためにのみトリガーを使用することを強くお薦めします。

トリガーを過剰に使用すると相互依存関係が複雑になる可能性があります。また、大規模なアプリケーションでは管理が困難になります。たとえば、トリガーが起動されると、そのトリガー・アクションに含まれるSQL文によって他のトリガーが起動されてトリガーの連鎖状態になり、意図しない影響が起きる可能性があります。

関連情報:

特定の状況で自動的に)。

トリガーのタイプ

トリガーは、起動方法と実行するアクションのタイプに応じて分類できます。

Oracle Databaseは次のタイプのトリガーをサポートしています。

関連情報:

特定の状況で自動的に)。

トリガーのタイミング

トリガー・アクションは、トリガー・アクションがトリガー文の前または後に実行されるかどうかにかかわらず、トリガー・タイミングを定義できます。

単純トリガーは表の単一のトリガーであり、次のいずれか1つのタイミング・ポイントに対してのみアクションを指定できます。

文および行のトリガーの場合、BEFOREトリガーは、セキュリティを高め、データベースに変更を加える前にビジネス・ルールを有効にできます。アクションのロギングには、AFTERトリガーが理想的です。

複合トリガーは、複数のタイミングポイントで起動する可能性があります。複合トリガーは、様々なタイミング・ポイントに対して実装するアクションで共通データを共有するためのアプローチをプログラム化する際に役立ちます。

複合トリガーについて学習するには、関連項目: Oracle AI Database PL/SQL言語リファレンスを参照してください。

特定の状況で自動的に)。

トリガーの作成

CREATE TRIGGER文はデータベース・トリガーを作成または置き換えます。

PL/SQLトリガーの一般的な構文形式は次のとおりです。

CREATE TRIGGER trigger_name
  triggering_statement
  [trigger_restriction]
BEGIN
 triggered_action;
END;

PL/SQLトリガーの基本的なコンポーネントは次のとおりです。

関連情報:

特定の状況で自動的に)。

例: CREATE TRIGGER文

この例では、明細項目表でINSERT文、UPDATE文またはDELETE文が実行されると起動されるトリガーを作成します。

次の文を使用してorders表およびlineitems表を作成するとします。orders表は一意のオーダーごとに行を保持するのに対して、lineitems表は1つのオーダー内の項目ごとに行を保持します。

CREATE TABLE orders
( order_id NUMBER PRIMARY KEY,
  /* other attributes */
  line_items_count NUMBER DEFAULT 0 );

CREATE TABLE lineitems
( order_id REFERENCES orders,
  seq_no   NUMBER,
  /* other attributes */
  CONSTRAINT lineitems PRIMARY KEY(order_id,seq_no) );

次の文で、orders表内のオーダーの項目数を自動的に更新するサンプル・トリガーを作成します。

CREATE OR REPLACE TRIGGER lineitems_trigger
  AFTER INSERT OR UPDATE OR DELETE ON lineitems
  FOR EACH ROW
BEGIN
  IF (INSERTING OR UPDATING)
  THEN
    UPDATE orders SET line_items_count = NVL(line_items_count,0)+1
    WHERE order_id = :new.order_id;
  END IF;
  IF (DELETING OR UPDATING)
  THEN
    UPDATE orders SET line_items_count = NVL(line_items_count,0)-1
    WHERE order_id = :old.order_id;
  END IF;
END;
/

lineitems_triggerでトリガーを実行する文は、lineitems表に対するINSERT、UPDATEまたはDELETEです。トリガー制限はありません。変更される行ごとに、トリガーが起動されます。トリガーは、トリガーを実行する文によって影響を受ける現在の行の、新旧の列値にアクセスできます。表の変更対象の列ごとに、古い値(:old)および新しい値(:new)の2つの相関名があります。セッションで、あるオーダーについてlineitemsの行を更新または挿入すると、このアクションの後、トリガーによってこのオーダーの項目数が計算され、orders表の数値が更新されます。

例: 行レベル・トリガーの起動

このシナリオでは、顧客は2つのオーダーを開始し、オーダーの明細項目を追加および削除します。

シナリオは、「例: CREATE TRIGGER文」で作成されたトリガーに基づいています。

表3行レベル・トリガーの使用例

SQL文 トリガーされるSQL文 摘要
SQL> INSERT INTO orders
 (order_id) VALUES (78);

  1 row created. 
 

顧客がID78のオーダーを作成します。この時点で、顧客のオーダーに項目はありません。

lineitems表ではアクションが実行されないため、このトリガーは起動されません。

SQL> INSERT INTO orders
 (order_id) VALUES (92);

  1 row created. 
 

顧客がID92の別のオーダーを作成します。この時点で、顧客のオーダーに項目はありません。

lineitems表ではアクションが実行されないため、このトリガーは起動されません。

SQL> INSERT INTO lineitems
  (order_id, seq_no)
 VALUES (78,1);

  1 row created. 
UPDATE orders
  SET line_items_count =
  NVL(NULL,0)+1
 WHERE order_id = 78;

顧客がオーダー78に項目を追加します。

INSERTによって、トリガーが起動されます。トリガーされた文が、オーダー78の明細項目数を0から1に増やします。

SQL> INSERT INTO lineitems
  (order_id, seq_no)
  VALUES (78,2);

  1 row created. 
UPDATE orders
  SET line_items_count =
  NVL(1,0)+1
 WHERE order_id = 78; 

顧客がオーダー78にさらに項目を追加します。

INSERTによって、トリガーが起動されます。トリガーされた文が、オーダー78の明細項目数を1から2に増やします。

SQL> SELECT * FROM orders;

  ORDER_ID LINE_ITEMS_COUNT
 --------- ----------------
 78 2
 92 0
  顧客が2つのオーダーのステータスを問い合せます。オーダー78には2つの項目があります。オーダー92には項目がありません。
SQL> SELECT * FROM lineitems;

  ORDER_ID SEQ_NO
 ---------- ----------
 78 1
 78 2
  顧客は明細項目のステータスを問い合せます。各項目は注文IDと順序番号によって一意に識別されます。
SQL> UPDATE lineitems
  SET order_id = 92;

  2 rows updated.
UPDATE orders
  SET line_items_count =
  NVL(NULL,0)+1
 WHERE order_id = 92;

  UPDATE orders
  SET line_items_count =
  NVL(2,0)-1
 WHERE order_id = 78; 
UPDATE orders
  SET line_items_count =
  NVL(1,0)+1
 WHERE order_id = 92;

  UPDATE orders
  SET line_items_count =
  NVL(1,0)-1
 WHERE order_id = 78;

顧客がオーダー78の明細項目をオーダー92に移動します。

UPDATE文はlineitems表の2つの行を変更します。このとき、各行に1回ずつトリガーが起動されます。

トリガーが起動されるたびに、トリガー内の両方のIF条件が満たされます。最初の条件はオーダー92の数を増加させるのに対して、2番目の条件はオーダー78の数を減少させます。これにより、合計4つのUPDATE文が実行されます。

SQL> SELECT * FROM orders;

  ORDER_ID LINE_ITEMS_COUNT
 --------- ----------------
 78 0
 92 2
  顧客が2つのオーダーのステータスを問い合せます。実際の結果としては、オーダー92の明細項目数が0から2に増加し、オーダー78の数が2から0に減少しています。
SQL> SELECT * FROM lineitems;

  ORDER_ID SEQ_NO
 ---------- ----------
 92 1
 92 2
  顧客は明細項目のステータスを問い合せます。各項目は注文IDと順序番号によって一意に識別されます。
SQL> DELETE FROM lineitems;

  2 rows deleted.
UPDATE orders
  SET line_items_count =
  NVL(2,0)-1
 WHERE order_id = 92;

  UPDATE orders
  SET line_items_count =
  NVL(1,0)-1
 WHERE order_id = 92;

顧客はすべてのオーダーからすべての明細項目を削除します。

DELETE文はlineitems表の2つの行を変更し、このとき、各行に1回ずつトリガーが起動されます。トリガーの起動ごとに、トリガーの片方のIF条件のみが満たされます。毎回、条件によってオーダー92の数を1つずつ減らします。これにより、合計2つのUPDATE文が実行されます。

SQL> SELECT * FROM orders;

  ORDER_ID LINE_ITEMS_COUNT
 --------- ----------------
 78 0
 92 0

  SQL> SELECT * FROM lineitems;

  no rows selected
 

顧客が2つのオーダーのステータスを問い合せます。どちらのオーダーにも明細項目がありません。

また、顧客は明細項目のステータスも問い合せます。項目は存在しません。

トリガーの実行

Oracle Databaseは、サブプログラムの実行と同じステップを使用してトリガーを内部的に実行します。

両者の唯一の違いは、ユーザー・アカウントがトリガー文を実行する権限を持っていれば、トリガーを起動する権限が付与されることです。この例外を除いて、データベースはストアド・サブプログラムと同じ方法でトリガーを検証して実行します。

トリガー実行についてさらに学習するには、『Oracle AI Database PL/SQL言語リファレンス』を参照してください。

特定の状況で自動的に)。

トリガーの記憶域

Oracle Databaseでは、PL/SQLトリガーは、PL/SQLのストアド・プロシージャと同じように、コンパイル済形式でデータベース・スキーマに格納されます。

CREATE TRIGGER文がコミットすると、コンパイルされたPL/SQLコードがデータベースに格納されます。共有プールからPL/SQLトリガーのソース・コードが削除されます。

次の図に示すデータベース・アプリケーションには、PL/SQLトリガーを暗黙のうちに起動するSQL文が含まれています。トリガーは、それに対応付けられている表とは別々に格納されています。

図7: トリガー

画像cncpt076.gifの説明

Javaトリガーは、PL/SQLトリガーと同じ方法で格納されます。ただし、Javaトリガーでは、CALL文を使用して、個別にコンパイルされたJavaコードを参照します。このため、Javaトリガーを作成する場合は、JavaコードおよびこのJavaコードを参照するトリガーを作成することが必要となります。

トリガーのコンパイルおよび格納について学習するには、『Oracle AI Database PL/SQL言語リファレンス』も参照してください。

特定の状況で自動的に)。