resp := UTL_HTTP.GET_RESPONSE(req); nfresults VALUES(SYSDATE, tblname, resp.reason_phrase);
「リスト 1」に示すように、sendNotification は、UTL_HTTP.BEGIN_REQUEST 関数によって発行される HTTP リクエストの形式でクライアントに通知メッセージを送信します。この URL には、ORDERS テーブル内の変更された行の order_id が含まれています。次に、UTL_HTTP.GET_RESPONSE を使用して、クライアントから送信された応答情報を取得します。実際、sendNotification はクライアントから返された応答全体を処理する必要はなく、RESP レコードのreason_phrase フィールドに格納されている短いメッセージ (ステータス コードを説明する) を取得するだけです。
これで、上で説明した sendNotification プロシージャを利用してクライアントに変更通知を送信する通知ハンドラーを作成できます。リスト 2 の PL/SQL プロシージャ order_nf_callback を見てみましょう。
リスト 2. OE.ORDERS テーブルへの変更の通知を処理する通知ハンドラー
numtables NUMBER;
row_id VARCHAR2(12);
url VARCHAR2(256);キャッシュ/dropResults.php?order_no=';
BEGIN
event_type := ntfnds.event_type;
numtables := ntfnds.numtables; THEN
FOR i IN 1..numtables
tbl名:= ntfnds .table_desc_array(i).table_name;
IF (bitand(ntfnds.table_desc_array(i).opflags,
DBMS_CHANGE_NOTIFICATION.ALL_ROWS) = 0) THEN
numrows := ntfnds.table_desc_array(i).numrows;
行数: = 0;
IF (tblname = 'OE.ORDERS') THEN
FOR j IN 1..numrows LOOP
row_id := ntfnds.table_desc_array(i).row_desc_array(j).row_id; ord_id FROM 注文 WHERE rowid = row_id;
END IF;
END IF;示されているように、これは通知ハンドラーは SYS.CHNF$_DESC オブジェクトをパラメーターとして受け取り、そのプロパティを使用して変更の詳細を取得します。この例では、この通知ハンドラーは、登録されたオブジェクトに対する DML または DDL の変更に応じてデータベースによってポストされた通知のみを処理し (つまり、通知タイプが EVENT_OBJCHANGE の場合のみ)、インスタンスの起動やインスタンスなどの他のデータベース イベントに関する情報は無視します。シャットダウン)通知。上記のバージョン以降、ハンドラーは、OE.ORDERS テーブル内の影響を受ける各行に対して発行された変更通知を処理できるようになりました。この記事の後半の「既存の登録へのテーブルの追加」セクションで、ハンドラーに数行のコードを追加して、OE.ORDER_ITEMS テーブル内の変更された行の通知を処理できるようにします。
変更通知の登録を作成する
通知ハンドラーを作成したら、それに対するクエリ登録を作成する必要があります。この例では、登録プロセス中に OE.ORDER テーブルに対してクエリを実行し、orders_nf_callback を通知ハンドラーとして指定する必要があります。通知メッセージで ROWID レベルの粒度を有効にするには、 DBMS_CHANGE_NOTIFICATION パッケージで QOS_ROWIDS オプションを指定する必要もあります。 「リスト 3」は、orders_nf_callback 通知ハンドラのクエリ登録を作成する PL/SQL ブロックです。
リスト 3. 通知ハンドラーのクエリ登録を作成する
コードをコピーする
コードは次のとおりです。
qosflags 番号;
BEGIN
qosflags := DBMS_CHANGE_NOTIFICATION.QOS_RELIABLE +
DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS := SYS.CHNF$_REG_INFO ('orders_nf_callback', qosflags, 0,0,0); MS_CHANGE_NOTIFICATION.NEW_REG_START (REGDS);
注文 WHERE ROWNUM/
;
この例では、ORDERS テーブルに対する登録を作成し、orders_nf_callback を通知ハンドラーとして使用します。ここで、DML または DDL ステートメントを使用して ORDERS テーブルを変更し、トランザクションをコミットすると、orders_nf_callback 関数が自動的に呼び出されます。たとえば、ORDERS テーブルに対して次の UPDATE ステートメントを実行し、トランザクションをコミットするとします。 ;
上記のトランザクションに応答してデータベースが通知をポストしたことを確認するには、nfresults テーブルをチェックします:
SELECT TO_CHAR(operdate, 'dd-mon-yy hh:mi:ss') operdate,
tblname, rslt_msg FROM nfresults;
結果は次のようになります:
OPERDATE TBLNAME RSLT_MSG
--------------------- ----------- ---------
02-mar-06 04:31:28 OE.ORDERS Not Found
02-mar-06 04:31:29 OE.ORDERS Not Found
上記を見れば一目瞭然です。結果は、orders_nf_callback は正常に動作しましたが、クライアント スクリプトが検索されなかったことがわかります。この例では、URL で指定された DropResults.php スクリプトを作成していないため、これは予期せぬことではありません。
既存の登録にテーブルを追加する
前のセクションでは、変更通知サービスを使用して、登録されたオブジェクト (上の例では ORDERS テーブル) が変更されたときにデータベースに通知させる方法を示しました。ただし、パフォーマンスの観点から、クライアント アプリケーションは、ORDERS テーブル自体ではなく、ORDER_ITEMS テーブルのクエリ結果セットをキャッシュすることを好む場合があります。これは、注文にアクセスするたびに ORDERS テーブルから 1 行だけを取得する必要があるためです。同時に複数の行を ORDER_ITEMS テーブルから取得する必要があります。実際には、注文には数十、場合によっては数百の項目が含まれる場合があります。
ORDERSテーブルのクエリはすでに登録されているので、ORDER_ITEMSテーブルのクエリを登録するために新たに登録を作成する必要はありません。代わりに、既存の登録を使用できます。これを行うには、まず既存の登録の ID を取得する必要があります。これを実現するには、次のクエリを実行できます:
SELECT regid, table_name FROM user_change_notification_regs; 結果は次のようになります:
REGID TABLE_NAME
----- --------------
241 OE.ORDERS
登録 ID を取得した後、以下に示すように DBMS_CHANGE_NOTIFICATION.ENABLE_REG 関数を使用して登録に新しいオブジェクトを追加できます:
コードをコピーします
コードは次のとおりです: ORD_ID NUMBER; BEGIN
DBMS_CHANGE_NOTIFICATION.ENABLE_REG(241);
SELECT order_id FROM order_items
完了!今後、データベースは、ORDERS および ORDER_ITEMS に対する変更に応じて通知を生成し、orders_nf_callback プロシージャを呼び出して通知を処理します。したがって、次の手順では、ORDER_ITEMS テーブルの DML 操作によって生成された通知を処理できるように、orders_nf_callback を編集します。ただし、orders_nf_callback プロシージャを再作成する前に、更新プロセス中に参照される次のテーブル タイプを作成する必要があります。コード行:
コードをコピー
コードは次のとおりです:
IF (tblname = 'OE.ORDERS') THEN
FOR j IN 1..numrows LOOP
row_id := ntfnds.table_desc_array(i ).row_desc_array(j).row_id; SELECT order_id INTO ord_id FROM order WHERE rowid = row_id; END IF; コードをコピーします
コードは次のとおりです:
IF (tblname = 'OE.ORDER_ITEMS') THEN
FOR rec IN (SELECT DISTINCT(o.order_id) o_id FROM
TABLE(CAST(ntfnds.table_desc_array(i)) .row_desc_array AS rdesc_tab)) t,
orders o, order_items d WHERE t.row_id = d.rowid AND d.order_id=o.order_id)
LOOP
sendNotification(url, tblname, rec.o_id);
END LOOP;
の場合は終了します
orders_nf_callbackを再作成したら、正しく動作するかテストする必要があります。これを行うには、ORDER_ITEMS テーブルに対して次の UPDATE ステートメントを実行し、トランザクションをコミットします:
UPDATE ORDER_ITEMS SET数量 = 160 WHERE order_id=2421 AND line_item_id=1;
UPDATE ORDER_ITEMS SET数量 = 160 WHERE order_id=2421 AND line_item_id =2;
COMMIT;
次に、次のように nfresults テーブルを確認します。 ; 出力は次のようになります:
OPERDATE RSLT_MSG
--------------- --------------
03-mar- 06 12 :32:27 Not Found
なぜ nfresults テーブルに 1 行しか挿入されなかったのか疑問に思われるかもしれません – 結局のところ、ORDER_ITEMS テーブルの 2 行を更新したのです。実際、更新された 2 つの行は同じ order_id を持っています。つまり、同じ注文に属しています。ここでは、クライアント アプリケーションが 1 つのステートメントを使用して注文のすべての明細を選択すると想定しているため、注文のどの明細が変更されたかを正確に知る必要はありません。代わりに、クライアントは、少なくとも 1 つの明細項目が変更、削除、または挿入された注文 ID を知る必要があります。
クライアントの構築
ORDERS テーブルと ORDER_ITEMS テーブルの登録を作成したので、これらのテーブルに保存されている注文とその品目にアクセスするクライアント アプリケーションによって変更通知がどのように使用されるかを見てみましょう。これを行うには、上記のテーブルに対するクエリの結果をキャッシュし、これらのテーブルへの変更に関する通知 (データベース サーバーから受信する) に応じて適切なアクションを実行する PHP アプリケーションを構築できます。簡単な方法は、キャッシュ データを最新の状態に保つための信頼できるメカニズムを提供する PEAR::Cache_Lite パッケージを使用することです。特に、Cache_Lite_Function クラス (PEAR::Cache_Lite パッケージの一部) を使用すると、関数呼び出しをキャッシュできます。
たとえば、データベース接続を確立し、データベースに対して select ステートメントを実行し、検索結果を取得し、最後に結果を配列として返すというタスクを実行する関数を作成できます。その後、Cache_Lite_Function インスタンスの call メソッドを通じて関数から返された結果配列をキャッシュし、バックエンド データベースではなくローカル キャッシュから読み取れるようにすることで、アプリケーションのパフォーマンスを大幅に向上させることができます。その後、キャッシュされたデータへの変更が通知されたら、Cache_Lite_Function インスタンスのdrop メソッドを使用して、期限切れのデータをキャッシュから削除します。
この記事の例を振り返ると、アプリケーションがデータベースと対話するために 2 つの関数を作成するとよいでしょう。最初の関数は ORDERS テーブルにクエリを実行し、指定された ID を持つ注文を返します。もう 1 つの関数は、ORDERS テーブルにクエリを実行します。 ORDER_ITEMS テーブルを参照し、その注文の品目を返します。リスト 4 は、getOrderFields 関数を含む getOrderFields.php スクリプトを示しています。この関数は注文 ID を受け取り、取得した注文のフィールドの一部を含む連想配列を返します。
リスト 4. 指定された順序のフィールドを取得します
コードをコピーします
コードは次のとおりです: //File:getOrderFields.php
require_once 'connect.php' ;
function getOrderFields($ order_no) {
if (!$rsConnection = GetConnection()){
return false;
}
$strSQL = "ORDER_DATE、CUSTOMER_ID、
ORDER_TOTAL FROM ORDERS WHERE order_id =:order_no ";
$rsStatement = oci_parse($rsConnection,$strSQL);
oci_bind_by_name($rsStatement, ":order_no", $order_no, 12);
if (!oci_execute($rsStatement)) {
$err = oci_error() ;
print $err['message'];
trigger_error('Query failed:' . $err['message']);
$results = oci_fetch_assoc($rsStatement);
}
?>
「リスト 5」は getOrderItems.php スクリプトです。スクリプトには getOrderItems 関数が含まれており、この関数は注文 ID を受け取り、注文の品目を表す行を含む 2 次元配列を返します。
リスト 5. 指定された順序の品目を取得します
コードをコピーします
コードは次のとおりです:
//File:getOrderItems.php require_once 'connect.php '; function getOrderItems( $order_no) {
if (!$rsConnection = GetConnection()){
return false;
}
$strSQL = "SELECT * FROM ORDER_ITEMS WHERE
order_id =:order_no ORDER BY line_item_id"; rsStatement = oci_parse($rsConnection ,$strSQL);
oci_bind_by_name($rsStatement, ":order_no", $order_no, 12);
if (!oci_execute($rsStatement)) {
$err = oci_error(); 'クエリが失敗しました:' . $err['message']);
$nrows = oci_fetch_all($rsStatement, $results);
? ;
上記の 2 つの関数には connect.php スクリプトが必要であることに注意してください。このスクリプトには、データベース接続を返す GetConnection 関数が含まれている必要があります。リスト 6 は connect.php スクリプトです:
リスト 6. データベース接続を取得します
コードをコピーします コードは次のとおりです:
//File:connect.php
関数 GetConnection() {
$dbHost = "dbserverhost";
$dbServiceName = "oe";
$dbConnStr = " (DESCRIPTION=(ADDRESS= (PROTOCOL=TCP)(HOST=".$dbHost.")
(PORT=".$dbHostPort."))(CONNECT_DATA=(SERVICE_NAME=".$dbServiceName.")))";
if(!$dbConn = oci_connect($usr,$pswd,$dbConnStr)) {
$err = oci_error();
trigger_error('接続に失敗しました ' .$err['message']);
}
return $dbConn ;
}
?>
データベースとの通信に必要な関数をすべて作成したので、Cache_Lite_Function クラスがどのように機能するかを見てみましょう。リスト 7 は、Cache_Lite_Function クラスを使用して上記の関数の結果をキャッシュする testCache.php スクリプトです。
リスト 7. PEAR::Cache_Lite キャッシュの使用
コードをコピーします
コードは次のとおりです。 require _once 'getOrderFields .php'; require_once 'Cache/Lite/Function.php'; $options = array( 'cacheDir' => '/tmp/',
'lifeTime' => 86400
) ;
if (! isset($_GET['order_no'])) {
die('order_no パラメーターは必要です')
$order_no=$_GET['order_no'];
$cache = new Cache_Lite_Function( $options);
if ($orderfields = $cache->call('getOrderFields', $order_no)){
print "
ORDER #$order_no
n"; ;";
print "
DATE: | ".$orderfields['ORDER_DATE']." |
";
print "
CUST_ID: | ".$orderfields['CUSTOMER_ID']." |
"
print "
TOTAL : | ".$orderfields['ORDER_TOTAL']." |
";
print "";
} else {
print " 注文フィールドの取得中に問題が発生しました!n";
$cache->drop('getOrderFields', $order_no);
if (list($nrows, $orderitems) ;call('getOrderItems ', $order_no)){
//print "
#$order_no
";
print "
"; ;tr>n" ;
while (list($key, $value) = each($orderitems)) {
print "$key | n";
}
print " for ($i = 0; $i print "";
print "".$orderitems['ORDER_ID'][$ i]." | ";
print "".$orderitems['LINE_ITEM_ID'][$i]." | "; orderitems['PRODUCT_ID' ][$i]."";
print "".$orderitems['UNIT_PRICE'][$i]." | "; " ".$orderitems['QUANTITY'][$i]." | ";
print "
"
}
print "
"; } else {
print "注文品目の取得中に問題が発生しました"
$cache->drop('getOrderItems', $order_no);
「リスト 7」の testCache.php スクリプトは、order_no URL パラメーター (OE.ORDER テーブルに格納されている注文 ID を表す) を使用して呼び出す必要があります。たとえば、ID 2408 の注文に関連する情報を取得するには、ブラウザに次の URL を入力する必要があります:
http://webserverhost/phpcache/testCache.php?order_no=2408 その結果、ブラウザは次の出力:
ORDER #2408
DATE: 29-JUN-99 06.59.31.333617 AM
CUST_ID: 166
TOTAL: 309
ORDER_ID LINE_ITEM_ID PRODUCT_ID UNIT_PRICE QUANTITY
2408 1 2751 61 3
2408 2 2761 26 1
2408 3 2783 10 10
これで、ブラウザの再読み込みボタンをクリックすると、testCache.php スクリプトは getOrderFields 関数と getOrderItems 関数を再度呼び出すことはなくなります。代わりに、ローカル キャッシュから結果を読み取ります。したがって、order_no=2108 を指定したすべての getOrderFields または getOrderItems 呼び出しは、今から 24 時間以内にローカル キャッシュによって満たされます (lifeTime が 86400 秒に設定されているため)。ただし、Cache_Lite_Function クラスは、指定されたパラメーターを使用して指定された関数でキャッシュが使用できるかどうかをテストする API を提供しないことに注意してください。したがって、アプリケーションが実際にキャッシュを読み取るのか、それとも同じパラメーターで呼び出されるたびに関数を実行するのかを判断するのは少し難しい場合があります。たとえば、上記の例では、キャッシュ メカニズムが正しく動作することを確認するために、connect.php スクリプトで指定された接続情報を一時的に変更して、データベース接続を確立できないようにすることができます。たとえば、間違ったデータベース サーバーのホスト名を指定します。 2108 testCache.php スクリプトを実行します。キャッシュが適切に機能している場合、ブラウザの出力は以前と同じになるはずです。
さらに、cache_Lite_FunctionクラスのコンストラクターにcacheDirオプション(この例では/tmp)の値として渡されるキャッシュディレクトリを確認することもできます。そのディレクトリには、cache_7b181b55b55aee36ad5e7bd9d5a091ec_3ad04d3024f4cd54296f75c92a359154 のような名前で作成した 2 つのキャッシュ ファイルがあります。 Windows ユーザーの場合は、%SystemDrive%temp ディレクトリを使用してキャッシュ ファイルを保存することをお勧めします。その場合、cacheDir オプションを /temp/ に設定する必要があります。
キャッシュ メカニズムが適切に動作していることを確認したら、データベース サーバーから受信した変更通知を処理するための PHP を作成できます。 「リスト 8」は、dropResult.php スクリプトです。データベース サーバーは、ORDERS テーブルと ORDER_ITEMS テーブルへの変更に応じてこのスクリプトを呼び出します。
リスト 8. データベースサーバーから受信した変更通知の処理
コードをコピー コードは次のとおりです:
//File:dropResults.php
require_once 'Cache/Lite/関数。php';
$options = array(
'cacheDir' => '/tmp/'
);
$cache = new Cache_Lite_Function($options); && isset ($_GET['table'])) {
if($_GET['table']=='ORDER_ITEMS'){
$cache->drop('getOrderItems', $_GET['order_no']) ;
}
if ($_GET['table']=='ORDERS'){
$cache->drop('getOrderFields', $_GET['order_no'])
}
?>
dropResult.php スクリプトを作成した後、通知ハンドラー (リスト 2 を参照) で指定された URL が正しいことを確認してください。次に、SQL*Plus または同様のツールで OE/OE として接続し、このセクションの前半で testCache.php スクリプトを通じてアクセスしたのと同じ順序 (ここでは ID 2408 の順序) に影響を与える UPDATE ステートメントを実行します。 Orders Set Order_mode = 'Direct' where Order_id = 2408;
Update Order_Items Set Quantity = 3 where Order_id = 2408 and line_id_id = 1; ATE Order_items Set Quantity = 1 Where Order_id = 2408 and LINE_ITEM_ID = 2;上記の更新では、この記事で前述した通知ハンドラーは、次の URL を使用して、dropResults.php スクリプトを 2 回実行します:
http://webserverhost/phpcache/dropResults.php?order_no=2408&table=ORDERS
http://webserverhost / phpcache/dropresults.php?order_no=2408&table=ORDER_ITEMS
「リスト 8」から、dropResult.php スクリプトがデータベース サーバーから変更通知を受信した後にキャッシュを更新していないことが明確にわかります。期限切れのデータを含むキャッシュ ファイルを削除するだけです。ここでキャッシュ ディレクトリを確認すると、order_no=2408 を指定して testCache.php スクリプトを実行したときに作成されたキャッシュ ファイルが消えていることがわかります。これが本質的に意味するのは、次回 testCache.php が注文 ID 2408 に関連するデータをリクエストするときに、ローカル キャッシュではなくバックエンド データベースからそのデータを取得するということです。
このメソッドは、アプリケーションによって要求された結果セットがアプリケーションで使用される前に変更される可能性がある状況で役立つことがわかります。この記事の例では、これは、特定の注文に関連するデータが、testCache.php がその注文にアクセスする前に複数回変更される可能性があることを意味します。このように、アプリケーションはデータベース サーバーから変更通知を受信した直後にキャッシュをフラッシュすることで、多くの不必要な作業を実行します。
ただし、dropResult.php スクリプトで変更通知を受け取った直後にキャッシュをフラッシュしたい場合は、drop メソッドを呼び出した後に Cache_Lite_Function インスタンスの call メソッドを呼び出し、両方の呼び出しに同じパラメーターを指定できます。この場合、dropResults.php が getOrderFields 関数と getOrderItems 関数を呼び出してキャッシュを更新できるように、getOrderFields.php スクリプトと getOrderItems.php スクリプトも必ず含める必要があります。 「リスト 9」は、変更されたdropResult.phpスクリプトです。
リスト 9. 変更通知を受け取ったらすぐにキャッシュを更新する
コードをコピー
コードは次のとおりです:
//File:dropResults.php require_once 'Cache/Lite/Function .php';
require_once 'getOrderFields.php';
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 86400
);
$cache = 新しい Cache_Lite_Function($options);
if (isset($_GET['order_no'])&& isset($_GET['table'])) {
if($_GET['table']==' ORDER_ITEMS '){
$cache->drop('getOrderItems', $_GET['order_no']);
$cache->call('getOrderItems', $_GET['order_no']); ( $_GET['table']=='ORDERS'){
$cache->drop('getOrderFields', $_GET['order_no']);
$cache->call('getOrderFields', $_GET [ 'order_no']);
}
}
?>
上記の方法は、ORDERS テーブルと ORDER_ITEMS テーブルに格納されたデータがほとんど変更されず、アプリケーションが頻繁にアクセスする場合に便利です。
概要
PHPアプリケーションがOracle Database 10g Release 2と対話する場合、データベース変更通知機能を利用できます。これにより、アプリケーションは、行われたリクエストに関連付けられたオブジェクトの変更に応じて通知を受け取ることができます。 DML が変更されます。この機能を使用すると、特定の期間中にアプリケーションのキャッシュを更新する必要がなくなります。代わりに、登録されたクエリの結果セットが変更された場合にのみ操作が実行されます。
http://www.bkjia.com/PHPjc/325869.html
www.bkjia.com
true
http://www.bkjia.com/PHPjc/325869.html
技術記事ただし、使用しているデータベースが Web サーバーとは異なるコンピュータ上にある場合は、データベースの結果セットをキャッシュすることをお勧めします。ただし、状況に応じて最適なキャッシュ ポリシーを決定してください...