Es ist bekannt, dass das Zwischenspeichern der Ergebnisse von Datenbankabfragen die Ausführungszeit von Skripten erheblich verkürzen und die Belastung des Datenbankservers minimieren kann. Diese Technik funktioniert sehr gut, wenn die von Ihnen verarbeiteten Daten im Wesentlichen statisch sind. Dies liegt daran, dass viele Datenanforderungen an die Remote-Datenbank letztendlich aus dem lokalen Cache erfüllt werden können, sodass keine Verbindung zur Datenbank hergestellt, die Abfrage ausgeführt und die Ergebnisse abgerufen werden müssen.
Das Zwischenspeichern des Datenbankergebnissatzes ist jedoch oft eine gute Idee, wenn sich die von Ihnen verwendete Datenbank auf einem anderen Computer als dem Webserver befindet. Es kann jedoch schwierig sein, die beste Caching-Strategie für Ihre Situation zu ermitteln. Beispielsweise ist für Anwendungen, bei denen es wichtig ist, den neuesten Datenbank-Ergebnissatz zu verwenden, ein zeitgesteuerter Caching-Ansatz (üblicherweise von Caching-Systemen verwendet, die davon ausgehen, dass der Cache jedes Mal neu generiert wird, wenn der Ablaufzeitstempel erreicht wird) möglicherweise keine zufriedenstellende Lösung . In diesem Fall benötigen Sie einen Mechanismus, der die Anwendung benachrichtigt, wenn sich die Datenbankdaten ändern, die die Anwendung zwischenspeichern muss, damit die Anwendung die zwischengespeicherten abgelaufenen Daten mit der Datenbank konsistent halten kann. In diesem Fall ist die Verwendung von „Datenbankänderungsbenachrichtigung“ sehr praktisch.
Erste Schritte mit der Datenbankänderungsbenachrichtigung
Die Verwendung der Datenbankänderungsbenachrichtigungsfunktion ist sehr einfach: Erstellen Sie einen Benachrichtigungshandler, der für die Benachrichtigung ausgeführt wird – eine gespeicherte PL/SQL-Prozedur oder eine Client-OCI-Callback-Funktion. Registrieren Sie dann eine Abfrage für die Datenbankobjekte, für die Sie Änderungsbenachrichtigungen erhalten möchten, sodass der Benachrichtigungshandler immer dann aufgerufen wird, wenn eine Transaktion ein darin enthaltenes Objekt ändert und festschreibt. Typischerweise sendet der Benachrichtigungshandler den Namen der geänderten Tabelle, die Art der vorgenommenen Änderung und optional die Zeilen-ID der geänderten Zeile an den Client-Listener, damit die Client-Anwendung in der Antwort entsprechende Aktionen ausführen kann.
Um zu verstehen, wie die Funktion „Datenbankänderungsbenachrichtigung“ funktioniert, betrachten Sie das folgende Beispiel. Gehen Sie davon aus, dass Ihre PHP-Anwendung auf in der Tabelle OE.ORDERS gespeicherte Bestellungen und auf in OE.ORDER_ITEMS gespeicherte Bestellelemente zugreift. Da sich Informationen zu aufgegebenen Bestellungen selten ändern, möchten Sie möglicherweise, dass Ihre Anwendung die Ergebnismengen von Abfragen für die Tabellen ORDERS und ORDER_ITEMS zwischenspeichert. Um den Zugriff auf veraltete Daten zu vermeiden, können Sie Datenbankänderungsbenachrichtigungen verwenden, mit denen Ihre Anwendung problemlos über Änderungen an den in den beiden obigen Tabellen gespeicherten Daten benachrichtigt werden kann.
Sie müssen dem OE-Benutzer die Systemberechtigung CHANGE NOTIFICATION und die Berechtigung EXECUTE ON DBMS_CHANGENOTIFICATION erteilen, bevor Sie Abfragen für die Tabellen ORDERS und ORDER_ITEMS registrieren können, um Benachrichtigungen zu erhalten und auf DML- oder DDL-Änderungen an diesen Tabellen zu reagieren. Führen Sie dazu den folgenden Befehl über ein SQL-Befehlszeilentool wie SQL*Plus aus.
CONNECT/AS SYSDBA;
Gewährung einer Änderungsmitteilung an oe;
GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO oe;
Stellen Sie sicher, dass der init.ora-Parameter job_queue_processes auf einen Wert ungleich Null gesetzt ist, um PL/SQL-Benachrichtigungen zu erhalten. Alternativ können Sie den folgenden ALTER SYSTEM-Befehl verwenden:
ALTER SYSTEM SET "job_queue_processes"=2; Anschließend können Sie nach der Verbindung als OE/OE einen Benachrichtigungshandler erstellen. Zunächst müssen Sie jedoch das Datenbankobjekt erstellen, das vom Benachrichtigungshandler verwendet wird. Beispielsweise möchten Sie möglicherweise eine oder mehrere Datenbanktabellen erstellen, in denen der Benachrichtigungshandler Registrierungsänderungen protokolliert. Im folgenden Beispiel erstellen Sie die Tabelle „nfresults“, um das Datum und die Uhrzeit der Änderung, den Namen der geänderten Tabelle und eine Meldung aufzuzeichnen, die angibt, ob der Benachrichtigungshandler die Benachrichtigungsnachricht erfolgreich an den Client gesendet hat.
CONNECT oe/oe;
CREATE TABLE nfresults (
operationDATE,
Tabellenname VARCHAR2(60),
rslt_msg VARCHAR2(100)
);
In einem realen Szenario müssen Sie möglicherweise weitere Tabellen erstellen, um Informationen wie Benachrichtigungsereignisse und die Zeilen-IDs geänderter Zeilen aufzuzeichnen. Für die Zwecke dieses Artikels reicht jedoch die Tabelle nfresults aus.
Verwenden von UTL_HTTP zum Senden von Benachrichtigungen an Clients.
Sie können auch eine oder mehrere gespeicherte PL/SQL-Prozeduren erstellen und diese gespeicherten Prozeduren vom Benachrichtigungshandler aufrufen, um so eine wartbarere und flexiblere Lösung zu erhalten. Beispielsweise möchten Sie möglicherweise eine gespeicherte Prozedur erstellen, die das Senden von Benachrichtigungsnachrichten an Clients implementiert. „Listing 1“ ist die PL/SQL-Prozedur sendNotification. Dieser Prozess verwendet das UTL_HTTPPL-Paket, um Änderungsbenachrichtigungen an Clientanwendungen zu senden.
Listing 1. Senden Sie eine Benachrichtigung an den Client mit UTL_HTTPCREATE
OR REPLACE PROCEDURE sendNotification(url IN VARCHAR2,
tblname IN VARCHAR2, order_id IN VARCHAR2) IS
req UTL_HTTP.REQ;
bzw. UTL_HTTP.RESP;
err_msg VARCHAR2(100);
Tabelle VARCHAR(60);
BEGINNEN
tbl:=SUBSTR(tblname, INSTR(tblname, '.', 1, 1)+1, 60);
BEGINNEN
req := UTL_HTTP.BEGIN_REQUEST(url||order_id||'&'||'table='||tbl);
resp := UTL_HTTP.GET_RESPONSE(req);
INSERT INTO nfresults VALUES(SYSDATE, tblname, resp.reason_phrase);
UTL_HTTP.END_RESPONSE(bzw.);
AUSNAHME, WENN ANDERE DANN
err_msg := SUBSTR(SQLERRM, 1, 100);
INSERT INTO nfresults VALUES(SYSDATE, tblname, err_msg);
ENDE;
BEGEHEN;
ENDE;
/
Wie in „Listing 1“ gezeigt, sendet sendNotification eine Benachrichtigungsnachricht an den Client in Form einer HTTP-Anfrage, die von der Funktion UTL_HTTP.BEGIN_REQUEST ausgegeben wird. Diese URL enthält die order_id der geänderten Zeile in der Tabelle ORDERS. Anschließend verwendet es UTL_HTTP.GET_RESPONSE, um die vom Client gesendeten Antwortinformationen abzurufen. Tatsächlich muss sendNotification nicht die gesamte vom Client zurückgegebene Antwort verarbeiten, sondern erhält nur eine kurze Nachricht (mit einer Beschreibung des Statuscodes), die im Feld reason_phrase des RESP-Datensatzes gespeichert ist.
Erstellen eines Benachrichtigungshandlers
Jetzt können Sie einen Benachrichtigungshandler erstellen, der mithilfe der oben beschriebenen sendNotification-Prozedur Änderungsbenachrichtigungen an Clients sendet. Werfen wir einen Blick auf die PL/SQL-Prozedurorders_nf_callback in „Listing 2“.
Listing 2. Benachrichtigungshandler, der Benachrichtigungen über Änderungen an der OE.ORDERS-Tabelle verarbeitet.
CREATE OR REPLACE PROCEDUREorders_nf_callback (ntfnds IN SYS.CHNF$_DESC) IS
Tabellenname VARCHAR2(60);
Numtables NUMBER;
event_type NUMBER;
row_id VARCHAR2(20);
Zahlen NUMBER;
ord_id VARCHAR2(12);
URL VARCHAR2(256) := 'http://webserverhost/phpcache/dropResults.php?order_no=';
BEGINNEN
event_type := ntfnds.event_type;
numtables := ntfnds.numtables;
WENN (event_type = DBMS_CHANGE_NOTIFICATION.EVENT_OBJCHANGE) DANN
FOR i IN 1..numtables LOOP
Tabellenname := ntfnds.table_desc_array(i).table_name;
IF (bitand(ntfnds.table_desc_array(i).opflags,
DBMS_CHANGE_NOTIFICATION.ALL_ROWS) = 0) DANN
numrows := ntfnds.table_desc_array(i).numrows;
ANDERS
Zahlen :=0;
ENDE WENN;
WENN (tblname = 'OE.ORDERS') DANN
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 FROMorders WHERE rowid = row_id;
sendNotification(url, tblname, ord_id);
ENDSCHLEIFE;
ENDE WENN;
ENDSCHLEIFE;
ENDE WENN;
BEGEHEN;
ENDE;
/
Wie in „Listing 2“ gezeigt, verwendet dieser Benachrichtigungshandler das SYS.CHNF$_DESC-Objekt als Parameter und verwendet dann seine Eigenschaften, um die Details der Änderung abzurufen. In diesem Beispiel verarbeitet dieser Benachrichtigungshandler nur Benachrichtigungen, die von der Datenbank als Reaktion auf DML- oder DDL-Änderungen an registrierten Objekten gesendet werden (d. h. nur, wenn der Benachrichtigungstyp EVENT_OBJCHANGE ist), und ignoriert Informationen über andere Datenbankereignisse wie Instanzstart oder Benachrichtigung über das Herunterfahren der Instanz. Ab der oben genannten Version kann der Handler Änderungsbenachrichtigungen verarbeiten, die für jede betroffene Zeile in der Tabelle OE.ORDERS ausgegeben werden. Später in diesem Artikel fügen Sie im Abschnitt „Hinzufügen einer Tabelle zu einer vorhandenen Registrierung“ einige Codezeilen zum Handler hinzu, damit dieser Benachrichtigungen für geänderte Zeilen in der Tabelle OE.ORDER_ITEMS verarbeiten kann.
Erstellen Sie eine Registrierung für Änderungsbenachrichtigungen.
Nachdem Sie einen Benachrichtigungshandler erstellt haben, müssen Sie eine Abfrageregistrierung dafür erstellen. Für dieses Beispiel müssen Sie während des Registrierungsprozesses eine Abfrage für die Tabelle OE.ORDER durchführen undorders_nf_callback als Benachrichtigungshandler angeben. Sie müssen außerdem die Option QOS_ROWIDS im Paket DBMS_CHANGE_NOTIFICATION angeben, um die Granularität auf ROWID-Ebene in Benachrichtigungsnachrichten zu aktivieren. „Listing 3“ ist ein PL/SQL-Block, der die Abfrageregistrierung für den Benachrichtigungshandler „orders_nf_callback“ erstellt.
Listing 3. Erstellen Sie die AbfrageregistrierungDECLARE
für den Benachrichtigungshandler
REGDS SYS.CHNF$_REG_INFO;
regid NUMBER;
ord_id NUMBER;
qosflags NUMBER;
BEGINNEN
qosflags := DBMS_CHANGE_NOTIFICATION.QOS_RELIABLE +
DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS;
REGDS := SYS.CHNF$_REG_INFO ('orders_nf_callback', qosflags, 0,0,0);
regid := DBMS_CHANGE_NOTIFICATION.NEW_REG_START (REGDS);
SELECT order_id INTO ord_id FROMorders WHERE ROWNUM<2;
DBMS_CHANGE_NOTIFICATION.REG_END;
ENDE;
/
In diesem Beispiel wird eine Registrierung für die Tabelle ORDERS erstellt undorders_nf_callback als Benachrichtigungshandler verwendet. Wenn Sie nun eine DML- oder DDL-Anweisung verwenden, um die ORDERS-Tabelle zu ändern und die Transaktion festzuschreiben, wird die Funktion „orders_nf_callback“ automatisch aufgerufen. Beispielsweise könnten Sie die folgende UPDATE-Anweisung für die Tabelle ORDERS ausführen und die Transaktion festschreiben:
UPDATE ORDERS SET order_mode = 'direct' WHERE order_id=2421;
UPDATE ORDERS SET order_mode = 'direct' WHERE order_id=2422;
BEGEHEN;
Um sicherzustellen, dass die Datenbank Benachrichtigungen als Reaktion auf die oben genannte Transaktion veröffentlicht hat, können Sie die nfresults-Tabelle überprüfen:
SELECT TO_CHAR(operdate, 'dd-mon-yy hh:mi:ss') operadate,
tblname, rslt_msg FROM nfresults;
Das Ergebnis sollte wie folgt aussehen:
OPERDATE TBLNAME RSLT_MSG
--------------------- ---------- ---------
02.03.06 04:31:28 OE.ORDERS nicht gefunden
02.03.06 04:31:29 OE.ORDERS nicht gefunden
Aus den obigen Ergebnissen geht klar hervor, dass „orders_nf_callback“ bereits funktioniert, das Client-Skript jedoch nicht gefunden wurde. Dies ist in diesem Beispiel nicht unerwartet, da Sie das in der URL angegebene Skript dropResults.php nicht erstellt haben. Anweisungen zum Skript dropResults.php finden Sie im Abschnitt „Erstellen des Clients“ weiter unten in diesem Artikel.
Hinzufügen einer Tabelle zu einer vorhandenen Registrierung
Im vorherigen Abschnitt wurde gezeigt, wie Sie den Change Notification Service verwenden, damit die Datenbank Sie benachrichtigt, wenn sich ein Registrierungsobjekt (im obigen Beispiel die Tabelle ORDERS) ändert. Aus Sicht der Leistung zieht es die Clientanwendung jedoch möglicherweise vor, die Abfrageergebnismenge der Tabelle ORDER_ITEMS statt der Tabelle ORDERS selbst zwischenzuspeichern, da sie bei jedem Zugriff auf die Bestellung nur eine Zeile aus der Tabelle ORDERS abrufen muss Gleichzeitig müssen mehrere Zeilen aus der Tabelle ORDER_ITEMS abgerufen werden. In Wirklichkeit kann eine Bestellung Dutzende oder sogar Hunderte von Einzelposten enthalten.
Da Sie bereits Abfragen für die Tabelle ORDERS registriert haben, müssen Sie keine Registrierung erstellen, um Abfragen für die Tabelle ORDER_ITEMS zu registrieren. Stattdessen können Sie eine bestehende Registrierung nutzen. Dazu müssen Sie zunächst die ID einer bestehenden Registrierung abrufen. Sie können die folgende Abfrage ausführen, um dies zu erreichen:
SELECT regid, table_name FROM user_change_notification_regs. Die Ergebnisse könnten wie folgt aussehen:
REGID TABLE_NAME
----- --------------
241 OE.BESTELLUNGEN
Nachdem Sie die Registrierungs-ID erhalten haben, können Sie der Registrierung mithilfe der Funktion DBMS_CHANGE_NOTIFICATION.ENABLE_REG wie folgt ein neues Objekt hinzufügen:
DECLARE
ord_id NUMBER;
BEGINNEN
DBMS_CHANGE_NOTIFICATION.ENABLE_REG(241);
SELECT order_id INTO ord_id FROM order_items WHERE ROWNUM < 2;
DBMS_CHANGE_NOTIFICATION.REG_END;
ENDE;
/
Erledigt! Von nun an generiert die Datenbank eine Benachrichtigung als Reaktion auf alle an ORDERS und ORDER_ITEMS vorgenommenen Änderungen und ruft die Prozedur „orders_nf_callback“ auf, um die Benachrichtigung zu verarbeiten. Daher besteht der nächste Schritt darin,orders_nf_callback so zu bearbeiten, dass es Benachrichtigungen verarbeiten kann, die durch DML-Vorgänge in der Tabelle ORDER_ITEMS generiert werden. Bevor Sie jedoch die Prozedur „orders_nf_callback“ neu erstellen, müssen Sie den folgenden Tabellentyp erstellen, auf den während des Aktualisierungsprozesses verwiesen wird:
CREATE TYPE rdesc_tab AS TABLE OF SYS.CHNF$_RDESC; Kehren Sie dann nach der folgenden Zeile von zu Listing 2 zurück Code:
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 FROMorders WHERE rowid = row_id;
sendNotification(url, tblname, ord_id);
ENDSCHLEIFE;
ENDE WENN;
Fügen Sie den folgenden Code ein:
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,
Bestellungen o, order_items d WHERE t.row_id = d.rowid AND d.order_id=o.order_id)
SCHLEIFE
sendNotification(url, tblname, rec.o_id);
ENDSCHLEIFE;
ENDE WENN;
Nachdem Sie „orders_nf_callback“ neu erstellt haben, müssen Sie testen, ob es ordnungsgemäß funktioniert. Dazu können Sie die folgende UPDATE-Anweisung für die Tabelle ORDER_ITEMS ausführen und die Transaktion festschreiben:
UPDATE ORDER_ITEMS SET amount = 160 WHERE order_id=2421 AND line_item_id=1;
UPDATE ORDER_ITEMS SET amount = 160 WHERE order_id=2421 AND line_item_id=2;
BEGEHEN;
Überprüfen Sie dann die nfresults-Tabelle wie folgt:
SELECT TO_CHAR(operdate, 'dd-mon-yy hh:mi:ss') operadate,
rslt_msg FROM nfresults WHERE tblname = 'OE.ORDER_ITEMS'; Die Ausgabe könnte so aussehen:
OPERDATE RSLT_MSG
-----------------------------------
03.03.06 12:32:27 Nicht gefunden
Sie fragen sich vielleicht, warum nur eine Zeile in die Tabelle nfresults eingefügt wurde – schließlich haben Sie zwei Zeilen in der Tabelle ORDER_ITEMS aktualisiert. Tatsächlich haben die beiden aktualisierten Zeilen dieselbe order_id – d. h. sie gehören zur selben Bestellung. Hier gehen wir davon aus, dass die Clientanwendung eine einzige Anweisung verwendet, um alle Einzelposten einer Bestellung auszuwählen, sodass sie nicht genau wissen muss, welche Einzelposten einer Bestellung geändert wurden. Stattdessen muss der Kunde die Bestell-ID kennen, in der mindestens eine Position geändert, gelöscht oder eingefügt wurde.
Erstellen des Clients
Nachdem Sie nun Registrierungen für die Tabellen ORDERS und ORDER_ITEMS erstellt haben, werfen wir einen Blick darauf, wie Änderungsbenachrichtigungen von Clientanwendungen verwendet werden, die auf in diesen Tabellen gespeicherte Bestellungen und deren Einzelposten zugreifen. Zu diesem Zweck können Sie eine PHP-Anwendung erstellen, die die Ergebnisse von Abfragen für die oben genannten Tabellen zwischenspeichert und als Reaktion auf Benachrichtigungen über Änderungen an diesen Tabellen (die vom Datenbankserver empfangen werden) entsprechende Maßnahmen ergreift. Eine einfache Möglichkeit ist die Verwendung des PEAR::Cache_Lite-Pakets, das Ihnen einen zuverlässigen Mechanismus bietet, um Cache-Daten auf dem neuesten Stand zu halten. Insbesondere können Sie die Klasse Cache_Lite_Function (Teil des Pakets PEAR::Cache_Lite) verwenden, mit der Sie Funktionsaufrufe zwischenspeichern können.
Sie können beispielsweise eine Funktion erstellen, die die folgenden Aufgaben ausführt: eine Datenbankverbindung herstellt, eine Select-Anweisung für die Datenbank ausführt, die Suchergebnisse abruft und die Ergebnisse schließlich als Array zurückgibt. Anschließend können Sie die von der Funktion zurückgegebenen Ergebnisarrays über die Aufrufmethode der Cache_Lite_Function-Instanz zwischenspeichern, sodass sie aus dem lokalen Cache und nicht aus der Back-End-Datenbank gelesen werden können, was die Leistung Ihrer Anwendung erheblich verbessern kann. Wenn Sie dann über Änderungen an den zwischengespeicherten Daten benachrichtigt werden, verwenden Sie die Drop-Methode der Cache_Lite_Function-Instanz, um die abgelaufenen Daten im Cache zu löschen.
Zurück zum Beispiel in diesem Artikel: Möglicherweise möchten Sie zwei Funktionen für Ihre Anwendung erstellen, um mit der Datenbank zu interagieren: Die erste Funktion fragt die Tabelle ORDERS ab und gibt die Bestellungen mit der angegebenen ID zurück, während die andere Funktion ORDER_ITEMS abfragt Tabelle und Rückgabe Gibt die Einzelposten für diese Bestellung zurück. „Listing 4“ zeigt das Skript getOrderFields.php, das die Funktion getOrderFields enthält, die eine Bestell-ID akzeptiert und ein assoziatives Array zurückgibt, das einige Felder der abgerufenen Bestellung enthält.
Listing 4. Holen Sie sich die Felder der angegebenen Reihenfolge
<?php
//Datei:getOrderFields.php
require_once 'connect.php';
Funktion getOrderFields($order_no) {
if (!$rsConnection = GetConnection()){
return false;
}
$strSQL = "SELECT TO_CHAR(ORDER_DATE) 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('Abfrage fehlgeschlagen:' . $err['message']);
return false;
}
$results = oci_fetch_assoc($rsStatement);
$results zurückgeben;
}
?>
„Listing 5“ ist das Skript getOrderItems.php. Das Skript enthält die Funktion getOrderItems, die eine Bestell-ID akzeptiert und ein zweidimensionales Array zurückgibt, das Zeilen enthält, die die Einzelposten der Bestellung darstellen.
Listing 5. Holen Sie sich die Einzelposten der angegebenen Reihenfolge
<?php
//Datei:getOrderItems.php
require_once 'connect.php';
Funktion 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();
trigger_error('Abfrage fehlgeschlagen:' . $err['message']);
return false;
}
$nrows = oci_fetch_all($rsStatement, $results);
Array zurückgeben ($nrows, $results);
}
?>
Beachten Sie, dass beide oben genannten Funktionen das Skript connect.php erfordern, das die Funktion GetConnection enthalten sollte, die die Datenbankverbindung zurückgibt. Listing 6 ist das Skript connect.php:
Listing 6. Datenbankverbindung herstellen
<?php
//Datei:connect.php
Funktion GetConnection() {
$dbHost = "dbserverhost";
$dbHostPort="1521";
$dbServiceName = "orclR2";
$usr = "oe";
$pswd = "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('Verbindung konnte nicht hergestellt werden ' .$err['message']);
return false;
}
return $dbConn;
}
?>
Nachdem Sie nun alle für die Kommunikation mit der Datenbank erforderlichen Funktionen erstellt haben, werfen wir einen Blick auf die Funktionsweise der Klasse Cache_Lite_Function. Listing 7 ist das testCache.php-Skript, das die Klasse Cache_Lite_Function verwendet, um die Ergebnisse der obigen Funktion zwischenzuspeichern.
von <?php
mit PEAR::Cache_Lite
//Datei:testCache.php
require_once 'getOrderItems.php';
require_once 'getOrderFields.php';
require_once 'Cache/Lite/Function.php';
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 86400
);
if (!isset($_GET['order_no'])) {
die('Der Parameter order_no ist erforderlich');
}
$order_no=$_GET['order_no'];
$cache = new Cache_Lite_Function($options);
if ($orderfields = $cache->call('getOrderFields', $order_no)){
print „<h3>ORDER #$order_no</h3>n“;
print „<Tabelle>“;
print "<tr><td>DATE:</td><td>".$orderfields['ORDER_DATE']."</td></tr>";
print "<tr><td>CUST_ID:</td><td>".$orderfields['CUSTOMER_ID']."</td></tr>";
print "<tr><td>TOTAL:</td><td>".$orderfields['ORDER_TOTAL']."</td></tr>";
print "</table>";
} anders {
print „Beim Abrufen der Bestellfelder ist ein Problem aufgetreten!n“;
$cache->drop('getOrderFields', $order_no);
}
if (list($nrows, $orderitems) = $cache->call('getOrderItems', $order_no)){
//print „<h3>LINE ITEMS IN ORDER #$order_no</h3>“;
print „<table border=1>“;
print "<tr>n";
while (list($key, $value) = every($orderitems)) {
print „<th>$key</th>n“;
}
print „</tr>n“;
for ($i = 0; $i < $nrows; $i++) {
print „<tr>“;
print "<td>".$orderitems['ORDER_ID'][$i]."</td>";
print "<td>".$orderitems['LINE_ITEM_ID'][$i]."</td>";
print "<td>".$orderitems['PRODUCT_ID'][$i]."</td>";
print "<td>".$orderitems['UNIT_PRICE'][$i]."</td>";
print "<td>".$orderitems['QUANTITY'][$i]."</td>";
print "</tr>";
}
print "</table>";
} anders {
print „Beim Abrufen der Bestellpositionen ist ein Problem aufgetreten“;
$cache->drop('getOrderItems', $order_no);
}
?>
Das testCache.php-Skript in „Listing 7“ sollte mit dem URL-Parameter order_no aufgerufen werden (der die in der Tabelle OE.ORDER gespeicherte Bestell-ID darstellt). Um beispielsweise Informationen zur Bestellung mit der ID 2408 abzurufen, geben Sie die folgende URL in Ihren Browser ein:
http://webserverhost/phpcache/testCache.php?order_no=2408 Als Ergebnis generiert der Browser die folgende Ausgabe :
BESTELL-Nr. 2408
DATUM: 29.06.99 06.59.31.333617 Uhr
CUST_ID: 166
GESAMT: 309
ORDER_ID LINE_ITEM_ID PRODUCT_ID UNIT_PRICE MENGE
2408 1 2751 61 3
2408 2 2761 26 1
2408 3 2783 10 10Wenn
Sie nun im Browser auf die Schaltfläche „Neu laden“ klicken, ruft das Skript testCache.php die Funktionen getOrderFields und getOrderItems nicht erneut auf. Stattdessen werden die Ergebnisse aus dem lokalen Cache gelesen. Daher wird jeder getOrderFields- oder getOrderItems-Aufruf mit order_no=2108 innerhalb von 24 Stunden vom lokalen Cache erfüllt (da lifeTime auf 86400 Sekunden eingestellt ist). Beachten Sie jedoch, dass die Klasse Cache_Lite_Function keine API bereitstellt, um zu testen, ob ein Cache für eine bestimmte Funktion mit bestimmten Parametern verfügbar ist. Daher kann es etwas schwierig sein, festzustellen, ob die Anwendung tatsächlich den Cache liest oder die Funktion immer noch bei jedem Aufruf mit denselben Parametern ausführt. Um beispielsweise im obigen Beispiel sicherzustellen, dass der Caching-Mechanismus ordnungsgemäß funktioniert, können Sie die im Skript connect.php angegebenen Verbindungsinformationen vorübergehend ändern, sodass die Datenbankverbindung nicht hergestellt werden kann. Geben Sie beispielsweise einen falschen Hostnamen für den Datenbankserver an , und verwenden Sie dann erneut order_no= 2108 Führen Sie das Skript testCache.php aus. Wenn das Caching ordnungsgemäß funktioniert, sollte die Ausgabe des Browsers dieselbe sein wie zuvor.
Darüber hinaus können Sie das Cache-Verzeichnis überprüfen, das als Wert der Option „cacheDir“ (in diesem Beispiel „/tmp“) an den Konstruktor der Klasse „Cache_Lite_Function“ übergeben wird. In diesem Verzeichnis finden Sie zwei Cache-Dateien, die Sie gerade erstellt haben und deren Namen etwa so lauten: Cache_7b181b55b55aee36ad5e7bd9d5a091ec_3ad04d3024f4cd54296f75c92a359154. Beachten Sie, dass Sie als Windows-Benutzer möglicherweise das Verzeichnis %SystemDrive%temp zum Speichern von Cache-Dateien verwenden möchten. Wenn ja, muss die Option „cacheDir“ auf /temp/ gesetzt werden.
Nachdem Sie überprüft haben, dass der Caching-Mechanismus ordnungsgemäß funktioniert, können Sie ein PHP erstellen, um die vom Datenbankserver empfangenen Änderungsbenachrichtigungen zu verarbeiten. „Listing 8“ ist das Skript dropResult.php. Der Datenbankserver ruft dieses Skript als Reaktion auf Änderungen an den Tabellen ORDERS und ORDER_ITEMS auf.
Listing 8. Umgang mit Änderungsbenachrichtigungen, die vom Datenbankserver
<?php
empfangen werden
//Datei:dropResults.php
require_once 'Cache/Lite/Function.php';
$options = array(
'cacheDir' => '/tmp/'
);
$cache = new Cache_Lite_Function($options);
if (isset($_GET['order_no'])&& isset($_GET['table'])) {
if($_GET['table']=='ORDER_ITEMS'){
$cache->drop('getOrderItems', $_GET['order_no']);
}
if ($_GET['table']=='ORDERS'){
$cache->drop('getOrderFields', $_GET['order_no']);
}
}
?>
Stellen Sie nach dem Erstellen des dropResult.php-Skripts sicher, dass die im Benachrichtigungshandler angegebene URL (siehe Listing 2) korrekt ist. Stellen Sie dann eine Verbindung als OE/OE in SQL*Plus oder einem ähnlichen Tool her und führen Sie UPDATE-Anweisungen aus, die sich auf dieselben Aufträge auswirken, auf die zuvor in diesem Abschnitt über das testCache.php-Skript zugegriffen wurde (hier der Auftrag mit der ID 2408):
UPDATE ORDERS SET order_mode = ' direkt' WHERE order_id=2408;
UPDATE ORDER_ITEMS SET amount = 3 WHERE order_id=2408 AND line_item_id=1;
UPDATE ORDER_ITEMS SET amount = 1 WHERE order_id=2408 AND line_item_id=2;
BEGEHEN;
Als Reaktion auf das obige Update führt der zuvor in diesem Artikel beschriebene Benachrichtigungshandler das Skript dropResults.php zweimal aus und verwendet dabei die folgenden URLs: http://webserverhost/phpcache/dropResults.php?order_no=2408&table=ORDERS
http://webserverhost/phpcache/dropresults.php?order_no=2408&table=ORDER_ITEMS
Aus „Listing 8“ können Sie deutlich erkennen, dass das Skript dropResult.php den Cache nicht leert, nachdem es die Änderungsbenachrichtigung vom Datenbankserver erhalten hat. Es werden lediglich Cache-Dateien gelöscht, die abgelaufene Daten enthalten. Wenn Sie also jetzt das Cache-Verzeichnis überprüfen, werden Sie feststellen, dass die Cache-Datei, die beim Ausführen des testCache.php-Skripts mit order_no=2408 erstellt wurde, verschwunden ist. Dies bedeutet im Wesentlichen, dass testCache.php das nächste Mal, wenn es Daten im Zusammenhang mit der Bestell-ID 2408 anfordert, diese Daten aus der Backend-Datenbank und nicht aus dem lokalen Cache erhält.
Diese Methode kann in Situationen nützlich sein, in denen sich der von der Anwendung angeforderte Ergebnissatz wahrscheinlich ändert, bevor die Anwendung ihn verwendet. Für das Beispiel dieses Artikels bedeutet dies, dass sich die Daten zu einer bestimmten Bestellung mehrmals ändern können, bevor testCache.php auf diese Bestellung zugreift. Auf diese Weise erledigt die Anwendung eine Menge unnötiger Arbeit, indem sie ihren Cache sofort nach dem Empfang von Änderungsbenachrichtigungen vom Datenbankserver leert.
Wenn Sie jedoch möchten, dass das Skript dropResult.php den Cache leert, sobald es über die Änderung informiert wird, können Sie die Aufrufmethode der Cache_Lite_Function-Instanz nach dem Aufruf der Drop-Methode aufrufen und dabei für beide Aufrufe dieselben Parameter angeben. In diesem Fall sollten Sie auch darauf achten, die Skripte getOrderFields.php und getOrderItems.php einzubinden, damit dropResults.php die Funktionen getOrderFields und getOrderItems aufrufen kann, um den Cache zu aktualisieren. „Listing 9“ ist das modifizierte dropResult.php-Skript.
Listing 9. Leeren Sie den Cache sofort nach Erhalt der Änderungsbenachrichtigung
<?php
//Datei:dropResults.php
require_once 'Cache/Lite/Function.php';
require_once 'getOrderItems.php';
require_once 'getOrderFields.php';
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 86400
);
$cache = new 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']);
}
if ($_GET['table']=='ORDERS'){
$cache->drop('getOrderFields', $_GET['order_no']);
$cache->call('getOrderFields', $_GET['order_no']);
}
}
?>
Der obige Ansatz kann nützlich sein, wenn sich die in den Tabellen ORDERS und ORDER_ITEMS gespeicherten Daten selten ändern und die Anwendung häufig darauf zugreift.
Zusammenfassung
Wenn Ihre PHP-Anwendung mit Oracle Database 10g Release 2 interagiert, können Sie die Funktion „Datenbankänderungsbenachrichtigung“ nutzen, die es Ihrer Anwendung ermöglicht, Benachrichtigungen als Reaktion auf DML-Änderungen an dem mit der gestellten Anforderung verknüpften Objekt zu erhalten. Mit dieser Funktion müssen Sie den Cache in Ihrer Anwendung nicht während eines bestimmten Zeitraums aktualisieren. Stattdessen wird der Vorgang nur ausgeführt, wenn sich die Ergebnismenge der registrierten Abfrage geändert hat.