Professional Documents
Culture Documents
Tworzenie
aplikacji webowych:
od przetwarzania danych
po Ajaksa
Autor: Yuli Vasiliev
Tumaczenie: Robert Grczyski, Artur Przybya
ISBN: 978-83-246-1974-0
Tytu oryginau: PHP Oracle Web Development:
Data processing, Security, Caching, XML,
Web Services, and Ajax
Format: 170x230, stron: 392
Poznaj niezwyke moliwoci duetu OraclePHP i twrz niezawodne aplikacje!
Jak poczy PHP i Oracle w celu uzyskania optymalnej wydajnoci
i niezawodnoci?
Jak wykorzystywa funkcje XML w PHP i Oracle?
Jak poprawi wydajno dziki zastosowaniu buforowania?
Baza Danych Oracle nie ma sobie rwnych pod wzgldem wydajnoci, niezawodnoci
oraz skalowalnoci. Natomiast skryptowy jzyk PHP dziki niezwykej prostocie
stosowania stanowi jedno z najpopularniejszych narzdzi budowania aplikacji
sieciowych nawet dla niezbyt dowiadczonych programistw. Budowanie i wdraanie
aplikacji PHP opartych na Oracle pozwala wic na optymalne poczenie potnych
moliwoci i solidnoci z atwoci uycia i krtkim czasem programowania.
Ksika PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych
po Ajaksa zawiera zilustrowany praktycznymi przykadami opis technologii oraz
wszystkich narzdzi potrzebnych, aby optymalnie wykorzysta moliwoci duetu
OraclePHP. Dziki temu podrcznikowi poznasz nowe funkcje PHP i bazy danych
Oracle; dowiesz si take, na czym polega programowanie procedur skadowanych
i obsuga transakcji. Nauczysz si tworzy niezawodne aplikacje i zapewnia im wysz
wydajno dziki mechanizmom buforowania, a take uywa technologii Ajax
z technologiami Oracle Database i funkcjami PHP w celu usprawnienia reakcji aplikacji
na dziaania uytkownika.
Poczenie PHP i Oracle
Przetwarzanie danych
Tworzenie i wywoywanie wyzwalaczy
Uywanie podprogramw skadowanych
Podejcie zorientowane obiektowo
Obsuga wyjtkw
Bezpieczestwo
Buforowanie
Aplikacje oparte na XML
Usugi sieciowe
Aplikacje oparte na Ajaksie
Spis treci
O autorze
11
O recenzencie
13
Wprowadzenie
15
16
17
17
18
19
20
20
21
21
22
22
23
23
23
25
25
25
26
27
28
28
29
30
30
31
Spis treci
31
31
32
34
35
37
40
40
41
42
42
43
45
45
46
46
51
51
52
53
54
54
56
56
57
57
58
59
59
60
61
63
63
65
66
67
68
71
72
72
73
74
75
Spis treci
76
76
77
79
80
Uywanie zcze
Wykorzystanie zalet widokw
80
83
Rozdzia 4. Transakcje
Oglny opis transakcji
Czym jest transakcja?
Czym s reguy ACID?
W jaki sposb transakcje dziaaj w Oracle?
Uywanie transakcji w aplikacjach PHP/Oracle
Strukturyzacja aplikacji PHP/Oracle w celu nadzorowania transakcji
Tworzenie kodu transakcyjnego
Nadzorowanie transakcji z poziomu PHP
Przenoszenie kodu transakcyjnego do bazy danych
83
84
85
87
87
89
90
94
95
97
98
99
99
100
103
104
104
105
106
107
110
113
113
119
Uywanie wyzwalaczy
Wycofanie na poziomie polecenia
119
120
123
123
127
Transakcje autonomiczne
Podsumowanie
127
129
132
135
137
138
138
139
141
142
Spis treci
Funkcjonalno i implementacja
Ponowne uywanie kodu
Obsuga wyjtkw
Modyfikacja istniejcej klasy w celu uycia wyjtkw
Rozrnienie midzy odmiennymi rodzajami bdw
Czy wyjtki koniecznie oznaczaj bdy?
Rozdzia 6. Bezpieczestwo
Zabezpieczanie aplikacji PHP/Oracle
Uwierzytelnianie uytkownikw
Oddzielenie zarzdzania bezpieczestwem od danych
Uywanie dwch schematw bazy danych w celu zwikszenia bezpieczestwa
Uywanie trzech schematw bazy danych w celu zwikszenia bezpieczestwa
Uywanie pakietw PL/SQL i funkcji tabelarycznych
w celu zapewnienia bezpiecznego dostpu do danych bazy danych
Uywanie atrybutu %ROWTYPE
Budowanie wasnego magazynu dla klasy PEAR::Auth
Testowanie systemu uwierzytelniania
Uywanie skrtw
Tworzenie skrtw hase
Modyfikacja systemu uwierzytelniania
w celu przeprowadzenia operacji tworzenia skrtu
Implementacja dokadnej kontroli dostpu za pomoc widokw bazy danych
Implementacja bezpieczestwa na poziomie kolumny za pomoc widokw
Maskowanie wartoci kolumn zwracanych aplikacji
Uywanie funkcji DECODE()
144
146
146
147
149
152
152
152
153
155
157
157
160
161
161
164
168
170
170
171
174
175
177
178
178
179
180
182
183
187
189
190
192
192
193
195
199
200
202
204
205
208
208
211
214
217
Spis treci
Rozdzia 7. Buforowanie
Buforowanie danych za pomoc Oracle i PHP
Buforowanie zapyta w serwerze bazy danych
219
220
220
220
222
224
226
228
232
236
236
237
240
242
244
244
245
246
247
249
250
251
253
255
256
256
257
259
260
265
265
268
269
271
272
273
273
275
277
281
285
285
286
289
Spis treci
Obsuga transakcji
Pobieranie danych za pomoc Oracle XQuery
Uywanie silnika XQuery do budowania danych XML
na podstawie danych relacyjnych
Rozoenie danych XML na posta danych relacyjnych
Podsumowanie
Podsumowanie
293
294
294
295
297
298
299
301
302
303
304
304
305
307
308
308
311
313
317
319
322
323
326
327
329
330
332
333
335
336
336
337
339
339
340
341
345
347
348
348
349
351
353
354
356
357
358
Spis treci
359
360
360
363
Skorowidz
363
365
365
367
367
368
369
369
369
371
372
373
373
374
375
4
Transakcje
Aby uzyska pewno, e uywane dane zawsze bd prawidowe, naley stosowa transakcje.
W skrcie: dostarczaj one mechanizm pozwalajcy na bezpieczne modyfikowanie danych przechowywanych w bazie danych poprzez przeniesienie bazy danych z jednego spjnego stanu
do kolejnego.
Klasycznym przykadem wykorzystania transakcji jest operacja bankowa, taka jak przelew
rodkw pieninych z jednego konta bankowego na inne. Zamy, e zachodzi potrzeba
przelania rodkw pieninych z konta oszczdnociowego na konto biece. W celu wykonania tej operacji trzeba bdzie przeprowadzi przynajmniej dwa kroki: zmniejszy wartoci
rodkw na koncie oszczdnociowym i zwikszy warto rodkw na koncie biecym.
Oczywiste jest, e w tego rodzaju sytuacji konieczne bdzie potraktowanie obu operacji jako
pojedynczej, aby zachowa saldo midzy kontami. Dlatego te adna z wymienionych operacji nie
moe zosta przeprowadzona oddzielnie musz by zakoczone obie lub adna z nich
programista musi zagwarantowa, e albo obie operacje zakocz si powodzeniem, albo
adna z nich nie bdzie przeprowadzona. W takiej sytuacji doskonaym rozwizaniem jest zastosowanie transakcji.
W rozdziale zostay omwione rne mechanizmy, ktre mog by uyte do przeprowadzania
transakcji za pomoc technologii PHP i Oracle. Zaczniemy od oglnego omwienia transakcji,
poniewa te informacje s bardzo wane w celu dokadnego zrozumienia sposobu dziaania
transakcji. Nastpnie zostay przedstawione szczegowy dotyczce stosowania na rne sposoby transakcji w aplikacjach PHP/Oracle.
Oznacza to, e wszystkie polecenia SQL zawarte w transakcji musz by z powodzeniem zakoczone, aby caa transakcja moga zosta zatwierdzona, dziki czemu zmiany przeprowadzone przez wszystkie operacje DML zostaj trwale przeprowadzone. Graficznie zostao to
pokazane na rysunku 4.1.
104
Rozdzia 4. Transakcje
Jak mona zobaczy na powyszym rysunku, polecenia SQL skadajce si na transakcj przenosz dane, na ktrych operuj z jednego stanu spjnoci do kolejnego. Transakcja musi zosta
zatwierdzona, aby wprowadzone przez ni zmiany zostay zastosowane w bazie danych i tym
samym przeniosy dane do kolejnego stanu spjnoci. W przeciwnym razie wszystkie polecenia SQL wykonane przez transakcj zostan wycofane, a dane pozostan w stanie, w ktrym
znajdoway si w chwili rozpoczcia transakcji.
Jeeli w trakcie wykonywania transakcji wystpi bd serwera, na przykad awaria sprztu
komputerowego, efekty transakcji zostan automatycznie wycofane. Jednak w pewnych sytuacjach programista moe chcie rcznie wycofa ukoczon (ale jeszcze nie zatwierdzon)
transakcj, w zalenoci od ustalonego warunku. Taka sytuacja zostaa pokazana graficznie na
rysunku 4.2.
Jak wida na powyszym rysunku, po zakoczeniu wykonywania wszystkich polece transakcji programista ma moliwo albo jej zatwierdzenia, albo wycofania.
Waciwo
Opis
Niepodzielno
Transakcja stanowi niepodzieln jednostk pracy. Oznacza to, e albo wszystkie operacje
w ramach transakcji zostan wykonane, albo nie bdzie wykonana adna z nich.
Spjno
Transakcja przenosi baz danych z jednego stanu spjnoci do kolejnego. Oznacza to,
e podczas przeprowadzania transakcji nie mog by zamane adne czynniki wpywajce
na spjno bazy danych. Jeeli transakcja zamie jakiekolwiek reguy spjnoci,
zostanie wycofana.
Izolacja
Trwao
Opis
Wydanie
polecenia COMMIT
Wydanie
polecenia ROLLBACK
Wydanie
polecenia DDL
Jeeli zostanie wydane polecenie DDL, Oracle w pierwszej kolejnoci zatwierdzi biec
transakcj, a nastpnie wykona i zatwierdzi polecenie DDL w nowej transakcji, ktra
skada si z pojedynczego polecenia.
Zamknicie
poczenia
Nieprawidowe
przerwanie
wykonywania
programu
106
Rozdzia 4. Transakcje
Jak mona si przekona na podstawie powyszej tabeli, transakcja zawsze bdzie albo zatwierdzona, albo wycofana, niezalenie od tego, czy zostanie wyranie zatwierdzona, czy wycofana.
Warto jednak zwrci uwag, e zawsze dobr praktyk jest wyrane zatwierdzanie lub wycofywanie transakcji zamiast polegania na zachowaniu domylnym Oracle. W rzeczywistoci
zachowanie domylne aplikacji transakcyjnej moe by rne w zalenoci od narzdzia stosowanego przez aplikacj do nawizywania poczenia z baz danych Oracle.
Na przykad jeeli skrypt PHP wsppracuje z baz danych Oracle za pomoc rozszerzenia
OCI8, nie mona liczy na to, e aktywna transakcja w poczeniu zostanie automatycznie
zatwierdzona po zamkniciu tego poczenia. W takim przypadku po zamkniciu poczenia
lub zakoczeniu wykonywania skryptu aktywna transakcja zostanie wycofana.
Natomiast jeeli rozczenie z baz danych nastpi po wydaniu polecenia DISCONNECT z poziomu
narzdzia SQL*Plus lub nastpi poczenie z baz jako inny uytkownik uywajcy polecenia
CONNECT bd sesja SQL*Plus zostanie zamknita w wyniku wydania polecenia EXIT, aktywna
transakcja w poczeniu bdzie zatwierdzona.
Aby unikn nieoczekiwanego zachowania w aplikacji, zawsze dobrze jest wyranie zatwierdza bd
wycofywa transakcje, zamiast polega na zachowaniu domylnym aplikacji transakcyjnej.
107
108
Rozdzia 4. Transakcje
W powyszym skrypcie zdefiniowano zapytanie zwracajce liczb rekordw, ktre przedstawiaj pracownikw zatrudnionych jako menederowie magazynu. W zapytaniu uyto funkcji
count() w celu obliczenia liczby rekordw speniajcych kryteria zdefiniowane w klauzuli
WHERE zapytania. W omawianym przypadku wartoci zwrotn funkcji count(*) jest liczba rekordw reprezentujcych pracownikw, dla ktrych warto pola job_id wynosi ST_MAN.
W skrypcie liczba rekordw przedstawiajcych menederw magazynu jest pobierana z bufora wynikowego za pomoc poczenia funkcji oci_fetch() i oci_result(). Nie trzeba w tym
przypadku stosowa ptli, poniewa zapytanie zwraca tylko jeden rekord zawierajcy pojedyncze pole o nazwie num_rows.
Nastpnie wykonywane jest zapytanie uaktualniajce kolumn salary tabeli employees. Uaktualnienie polega na zwikszeniu pensji menederw magazynu o 10%. Zapytanie uaktualnia
wysoko pensji tylko wtedy, gdy nowa wysoko pensji bdzie miecia si midzy minimaln i maksymaln wysokoci pensji ustalonymi dla menederw magazynu i zdefiniowanymi
w tabeli jobs.
W omawianym przykadzie polecenie UPDATE jest wykonywane w trybie wykonywania OCI_
DEFAULT. W ten sposb nastpuje rozpoczcie transakcji, ktra pozwala programicie w dalszej
czci skryptu na wyrane zatwierdzenie lub wycofanie zmian wprowadzonych przez polecenie UPDATE. Interesujc kwesti, na ktr warto zwrci uwag, jest fakt, e domylny tryb
wykonywania to OCI_COMMIT_ON_SUCCESS, w ktrym polecenie jest zatwierdzane automatycznie, jeli jego wykonywanie zakoczy si powodzeniem.
Wedug dokumentacji Oracle aplikacja zawsze powinna wyranie zatwierdza bd wycofywa transakcj przed zakoczeniem dziaania programu. Jednak podczas uywania rozszerzenia PHP OCI8 nie trzeba
tego robi, gdy polecenia SQL s wykonywane w trybie OCI_COMMIT_ON_SUCCESS. W wymienionym
trybie polecenie SQL jest zatwierdzane automatycznie, jeli jego wykonanie zakoczy si powodzeniem
(czyli w sytuacji podobnej do wyranego zatwierdzenia natychmiast po wykonaniu polecenia). Jeeli
bd serwera uniemoliwi zakoczenie powodzeniem wykonywania polecenia SQL, Oracle automatycznie
wycofa wszystkie zmiany wprowadzone przez nie.
W omawianym skrypcie funkcja oci_num_rows() jest wywoywana w celu uzyskania liczby rekordw zmodyfikowanych przez operacj UPDATE. Po poznaniu liczby rekordw przedstawiajcych menederw magazynu oraz liczby faktycznie uaktualnionych mona porwna te
wartoci i sprawdzi, czy s identyczne.
109
110
Rozdzia 4. Transakcje
zatwierdza bd wycofywa operacji UPDATE. Zamiast tego polecenie UPDATE mona wyda
w trybie OCI_COMMIT_ON_SUCCESS, ktry gwarantuje, e operacja zostanie automatycznie zatwierdzona, jeli jej wykonanie zakoczy si powodzeniem, a w przeciwnym razie wycofana.
Przedstawiony poniej skrypt prezentuje w dziaaniu now wersj polecenia UPDATE:
<?php
// Plik: transQuery.php.
if(!$dbConn = oci_connect('hr', 'hr', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Nie mona nawiza poczenia z baz danych: '
. $err['message'], E_USER_ERROR);
};
$jobno = 'ST_MAN';
$query = "
UPDATE (SELECT salary, job_id FROM employees WHERE
(SELECT count(*) FROM employees WHERE job_id=:jobid AND
salary*1.1 BETWEEN (SELECT min_salary FROM jobs WHERE
job_id=:jobid) AND
(SELECT max_salary FROM jobs WHERE
job_id=:jobid)) IN
(SELECT count(*) FROM employees WHERE job_id=:jobid)
) emp
SET emp.salary = salary*1.1
WHERE emp.job_id=:jobid";
$stmt = oci_parse($dbConn,$query);
oci_bind_by_name($stmt, ':jobid', $jobno);
if (!oci_execute($stmt, OCI_COMMIT_ON_SUCCESS)) {
$err = oci_error($stmt);
trigger_error('Wykonanie zapytania zakoczyo si niepowodzeniem: '
. $err['message'], E_USER_ERROR);
};
$updrows = oci_num_rows($stmt);
if ($updrows>0) {
print "Transakcja zostaa zatwierdzona.";
} else {
print "Transakcja zostaa wycofana.";
}
?>
W powyszym fragmencie kodu zdefiniowano polecenie UPDATE, ktre spowoduje uaktualnienie wszystkich rekordw reprezentujcych menederw magazynu poprzez zwikszenie wysokoci ich pensji o 10%, pod warunkiem e adna z nowych wartoci pensji nie przekroczy
maksymalnej pensji menedera magazynu zdefiniowanej w tabeli jobs. Jeeli chocia jedna
pensja przekroczy warto maksymaln, polecenie UPDATE nie uaktualni adnych rekordw.
Aby uzyska taki sposb dziaania, zamiast podawania tabeli employees w klauzuli polecenia
UPDATE uyte zostao polecenie SELECT, ktrego wartoci zwrotn s albo wszystkie rekordy
111
tabeli employees, albo aden z nich. Zaley to od tego, czy wszystkie rekordy speniajce warunek zdefiniowany w klauzuli WHERE polecenia UPDATE (w omawianym przypadku bd to
wszystkie rekordy reprezentujce menederw magazynu) mog by uaktualnione w taki sposb, aby kada nowa wysoko pensji nie przekraczaa wartoci maksymalnej pensji dla tego
stanowiska.
To polecenie SELECT jest uznawane za widok wewntrzny. W przeciwiestwie do zwykych widokw
omwionych w podrozdziale Wykorzystanie zalet widokw, znajdujcym si w rozdziale 3., widoki
wewntrzne nie s obiektami schematu bazy danych, ale podzapytaniami, ktre mog by stosowane
jedynie w ramach zawierajcych je polece za pomoc aliasw.
W omawianym przykadzie zastosowanie w poleceniu UPDATE wewntrznego widoku emp powoduje wyeliminowanie potrzeby wykonywania oddzielnego zapytania zwracajcego liczb
rekordw reprezentujcych menederw magazynu, a nastpnie sprawdzajcego, czy otrzymana liczba jest rwna liczbie rekordw faktycznie zmodyfikowanych przez polecenie UPDATE.
Teraz skrypt wykonuje tylko pojedyncze zapytanie SQL, dziki czemu wyranie skraca czas
wykonywania skryptu.
Omwiony skrypt jest dobrym przykadem pokazujcym korzyci, jakie mona odnie po przeniesieniu
kluczowej logiki biznesowej aplikacji PHP/Oracle z PHP do bazy danych Oracle. Tu zamiast uywania
dwch oddzielnych polece i analizowania ich wynikw w PHP zastosowane zostao tylko jedno polecenie SQL, ktre powoduje, e przetwarzanie danych zachodzi cakowicie w serwerze bazy danych.
Warto take zwrci uwag na uyty w skrypcie sposb wizania zmiennych. Zmienna PHP
jobno jest dowizywana do znacznika jobid uytego w poleceniu UPDATE. Interesujcy jest fakt, e
znacznik zmiennej wizanej jobid pojawia si w poleceniu czciej ni tylko jednokrotnie.
Inaczej ni w poprzednim przykadzie, w ktrym polecenie UPDATE byo wykonywane w trybie OCI_DEFAULT, wyranie rozpoczynajcym transakcj, nowa wersja skryptu wykonuje polecenie w trybie OCI_COMMIT_ON_SUCCESS. Operacja UPDATE jest wic automatycznie zatwierdzana, jeli jej wykonanie zakoczy si powodzeniem.
Jak wczeniej wspomniano, OCI_COMMIT_ON_SUCCESS jest trybem domylnym wykonywania polecenia. Oznacza to, e nie trzeba wyranie go okrela podczas wywoywania funkcji oci_execute().
W omawianym przykadzie zosta wyranie umieszczony w kodzie rdowym, aby zwrci na to uwag.
W poprzednim przykadzie nadal uywano funkcji oci_num_rows() w celu pobrania liczby rekordw zmodyfikowanych przez polecenie UPDATE. Jednak tym razem nie trzeba porwnywa
tej liczby z cakowit liczb rekordw reprezentujcych menederw magazynu, jak to miao
miejsce w przypadku poprzedniego skryptu. Wszystko, co trzeba zrobi, to prostu sprawdzi,
czy liczba rekordw zmodyfikowanych przez polecenie UPDATE jest wiksza ni 0.
112
Rozdzia 4. Transakcje
Jeeli liczba uaktualnionych rekordw jest wiksza ni 0, oznacza to, e operacja UPDATE zmodyfikowaa wszystkie rekordy reprezentujce menederw magazynu i zostaa z powodzeniem zatwierdzona. W takim przypadku naley wywietli uytkownikowi komunikat informujcy o zatwierdzeniu transakcji.
Jeeli liczba uaktualnionych rekordw wynosi 0, oznacza to, e operacja UPDATE nie zmodyfikowaa adnych rekordw. W takim przypadku naley wywietli uytkownikowi komunikat
informujcy o wycofaniu transakcji. Jednak w rzeczywistoci transakcja jest zatwierdzona, ale
aden rekord nie zosta zmodyfikowany przez operacj UPDATE.
113
rzecz biorc, celem tej analizy jest umoliwienie atwiejszego zrozumienia sposobu dziaania
transakcji Oracle w skryptach PHP, ktre wspdziaaj z baz danych za pomoc rozszerzenia OCI8.
W przykadzie, ktry zostanie omwiony poniej, uyto struktur danych, ktre zdefiniowano
w podrozdziale Przykad uycia podprogramu skadowanego, znajdujcym si w rozdziale 3.
Zanim jednak przejdziemy do przykadu, konieczne jest zmodyfikowanie wymienionych struktur
danych w przedstawiony poniej sposb. Te polecenia SQL mona wykona z poziomu narzdzia SQL*Plus po nawizaniu poczenia z baz danych jako usr/usr:
ALTER TABLE accounts
ADD (num_logons INT);
UPDATE accounts
SET num_logons = 0;
COMMIT;
DELETE logons;
ALTER TABLE logons
ADD CONSTRAINT log_time_day
CHECK (RTRIM(TO_CHAR(log_time, 'Day'))
NOT IN ('Saturday', 'Sunday'));
Wydanie polecenia ALTER TABLE w powyszym bloku kodu powoduje dodanie do tabeli accounts
kolumny num_logons typu INT. W nowej kolumnie bdzie przechowywana liczba udanych operacji
logowania kadego uytkownika. W tym celu gdy uwierzytelnienie uytkownika zakoczy si
powodzeniem, trzeba bdzie zwikszy liczb operacji logowania przechowywan w polu
num_logons.
Oczywicie, nadal mona si obej bez tej kolumny, wydajc wzgldem tabeli logons zapytanie podobne do poniszego:
SELECT count(*) FROM logons WHERE usr_id='bob';
Jednak wraz ze wzrostem liczby operacji logowania powysze zapytanie bdzie bardzo kosztowne jak na otrzymanie informacji o liczbie logowa przeprowadzonych przez danego uytkownika.
Po dodaniu komuny num_logons do tabeli accounts nowej kolumnie naley ustawi warto
pocztkow wynoszc 0. Ewentualnie wczeniejsze polecenie ALTER TABLE mona byo wyda
z uyciem klauzuli DEFAULT wzgldem kolumny num_logons, na przykad nastpujco:
ALTER TABLE accounts
ADD (num_logons INT DEFAULT 0);
W omawianym bloku kodu nastpuje wyrane zatwierdzenie transakcji, aby zmiany wprowadzone przez operacj UPDATE byy trwae.
W kolejnym kroku usuwane s wszystkie rekordy tabeli logons. Ten krok jest wymagany, aby
zagwarantowa prawidowe wykonanie polecenia check constraint zdefiniowanego w nastpnym
114
Rozdzia 4. Transakcje
kroku. W omawianym przykadzie mona pomin ten krok, jeeli tabela logons nie zawiera
rekordw utworzonych w sobot lub niedziel, co umoliwi prawidowe wykonanie polecenia
check constraint zdefiniowanego w kolejnym kroku. W przeciwnym razie podczas prby wykonania polecenia ALTER TABLE zostanie wywietlony nastpujcy komunikat bdu:
ERROR at line 2:
ORA-02293: cannot validate (USR.LOG_TIME_DAY) - check constraint violated
W skrypcie definicja check constraint obejmuje kolumn log_time tabeli logons. Wymienione ograniczenie uniemoliwia wstawianie nowych rekordw do tabeli logons w sobot lub
niedziel. Pozwala to na modyfikacj systemu uwierzytelniania w taki sposb, aby uniemoliwi uytkownikom logowanie si w soboty i niedziele, a tym samym pozwoli na logowanie
jedynie w dni robocze. W pniejszym czasie takie ograniczenie zawsze bdzie mona usun
poprzez wydanie nastpujcego polecenia:
ALTER TABLE logons DROP CONSTRAINT log_time_day;
Wrmy jeszcze do polecenia ALTER TABLE, ktre przedstawiono powyej. Warto zwrci
uwag na uyty format 'Day', podany jako drugi parametr funkcji TO_CHAR(). Nakazuje on
funkcji TO_CHAR() konwersj daty przechowywanej w polu log_time na posta dnia tygodnia.
Nastpnie stosowany jest operator NOT IN, ktry powoduje wykluczenie soboty (Saturday)
i niedzieli (Sunday) z listy dozwolonych dni.
Naley pamita, e baza danych Oracle rozrnia wielko liter podczas dopasowania. Dlatego te jeli podano 'Day' jako drugi parametr funkcji TO_CHAR(), dni tygodnia na licie znajdujcej si po prawej stronie operatora NOT IN naley poda jako: 'Saturday', 'Sunday'. Miayby one
posta 'SATURDAY', 'SUNDAY', gdyby drugi parametr funkcji TO_CHAR() zosta podany jako 'DAY'.
Powysze polecenia powoduj przeprowadzenie wszystkich wymaganych modyfikacji struktury bazy danych. Po ich wykonaniu mona wic przej do przykadu, ktrego zadaniem jest zilustrowanie sposobw tworzenia transakcji poprzez wykonanie polecenia DML w trybie OCI_DEFAULT,
a nastpnie wyranego zakoczenia tej transakcji po wykonaniu kolejnego polecenia, ale
w trybie OCI_COMMIT_ON_SUCCESS.
W rzeczywistoci oczywicie moe zaj potrzeba uycia wicej ni tylko dwch polece
w transakcji. W tym celu mona wykona w trybie OCI_DEFAULT wszystkie polecenia (poza ostatnim), ktre maj zosta zgrupowane w pojedynczej transakcji, a nastpnie wykona ostatnie
polecenie w trybie OCI_COMMIT_ON_SUCCESS transakcja bdzie zakoczona.
Graficzne przedstawienie caego procesu pokazano na rysunku 4.3.
Przedstawiony poniej skrypt prezentuje, w jaki sposb architektura pokazana na rysunku 4.3
moe by zaimplementowana w PHP. Warto zauway, e w przeciwiestwie do funkcji login(),
omwionej w podrozdziale Przykad uycia podprogramu skadowanego, ktry znajduje si
w rozdziale 3., przedstawiona poniej funkcja login() zatrzymuje wykonywanie i zwraca
115
Rysunek 4.3. Graficzne przedstawienie procesu wykonywania omwionego powyej bloku kodu
116
Rozdzia 4. Transakcje
?>
}
$num_logons=$arr['NUM_LOGONS']+1;
oci_free_statement($stmt);
$query = "UPDATE accounts SET num_logons = num_logons + 1";
$stmt = oci_parse($rsConnection,$query);
if (!oci_execute($stmt, OCI_DEFAULT)) {
$err = oci_error($stmt);
trigger_error('Operacja uaktualnienia zakoczya si niepowodzeniem: '
. $err['message'], E_USER_WARNING);
return false;
}
oci_free_statement($stmt);
$query = "INSERT INTO logons VALUES (:userid, SYSDATE)";
$stmt = oci_parse($rsConnection,$query);
oci_bind_by_name($stmt, ':userid', $usr);
if (!oci_execute($stmt, OCI_COMMIT_ON_SUCCESS)) {
$err = oci_error($stmt);
trigger_error('Operacja wstawienia zakoczya si niepowodzeniem: '
. $err['message'], E_USER_WARNING);
if ($err['code']=='02290'){
print "Nie mona nawiza poczenia w sobot lub niedziel.";
}
return false;
}
print "Witaj ".$arr['FULL_NAME']."<br/>";
print "Odwiedzie nas ju ".$num_logons." raz(y)";
session_start();
$_SESSION['user']=$usr;
return true;
117
Wykonanie polecenia INSERT w trybie OCI_COMMIT_ON_SUCCESS w omawianym skrypcie gwarantuje, e transakcja zostanie zatwierdzona w przypadku powodzenia operacji, a w przeciwnym razie wycofana. Oznacza to, e zmiany wprowadzone zarwno przez polecenie INSERT,
jak i UPDATE albo stan si trwae, albo bd wycofane.
Jak Czytelnik zapewne pamita, wartoci zwrotn funkcji oci_error() jest tablica asocjacyjna dwch elementw. Pierwszy z nich code zawiera kod bdu Oracle, a drugi, message
komunikat bdu opisujcy ten bd. W omawianym przykadzie nastpuje sprawdzenie,
czy kod bdu Oracle jest rwny 02290. Jeeli tak, oznacza to wystpienie bdu zwizanego
ze zamaniem naoonego ograniczenia. Poniewa w tabeli logons zdefiniowano tylko jedno
ograniczenie (uniemoliwiajce wstawiania nowych rekordw do tabeli logons w soboty i niedziele), mona poinformowa uytkownika o braku moliwoci uzyskania poczenia w sobot
bd niedziel.
Jeeli w omawianym skrypcie wykonanie polecenia INSERT zakoczy si niepowodzeniem,
nastpi zakoczenie dziaania funkcji login() wraz z wartoci zwrotn false. Wskazuje to
wywoujcemu j skryptowi, e uwierzytelnienie nie powiodo si. W przypadku uwierzytelnienia
zakoczonego powodzeniem mona podj odpowiednie dziaania, na przykad wywietli
komunikat powitania i utworzy now sesj.
Aby teraz zobaczy w dziaaniu nowo utworzon funkcj login(), mona wykorzysta poniszy prosty skrypt:
<?php
// Plik: testLoginTrans.php.
require_once "userLoginTrans.php";
if (login('bob','pswd')) {
if (isset($_SESSION['user'])) {
print '<p>'.'Twoja nazwa konta: '.$_SESSION['user'].'</p>';
} else {
print '<p>'.'Zmienna sesji przedstawiajca nazw konta
nie zostaa ustawiona.'.'</p>';
}
}else {
print '<p>'.'Uwierzytelnienie zakoczyo si niepowodzeniem'.'</p>';
}
?>
118
Rozdzia 4. Transakcje
Kade kolejne uruchomienie skryptu testLoginTrans.php w dniu roboczym spowoduje zwikszenie licznika odwiedzin strony przez uytkownika Bob Robinson. Jednak wykonanie tego
skryptu w sobot lub niedziel nie powoduje zwikszenia wartoci licznika. Powyszy test
udowadnia, e wszystko dziaa zgodnie z zaoeniami.
Uywanie wyzwalaczy
Prac mona rozpocz od zdefiniowania wyzwalacza BEFORE INSERT, obejmujcego tabel
logons, ktry bdzie automatycznie uaktualnia tabel accounts poprzez zwikszanie wartoci
pola num_logons w odpowiednim rekordzie. W ten sposb zostanie wyeliminowana potrzeba
wykonywania tej operacji UPDATE z poziomu kodu PHP.
Przedstawione poniej polecenie SQL powoduje utworzenie wyzwalacza. Naley je wykona
z poziomu narzdzia SQL*Plus po nawizaniu poczenia z baz danych jako usr/usr:
CREATE OR REPLACE TRIGGER logons_insert
BEFORE INSERT
ON logons
FOR EACH ROW
BEGIN
UPDATE accounts
SET num_logons = num_logons + 1
WHERE usr_id = :new.usr_id;
END;
/
119
return false;
}
oci_free_statement($stmt);
Warto zwrci uwag, e powysza modyfikacja funkcji login() nie wymaga zmiany istniejcego kodu, ktry implementuje t funkcj. Dlatego te w celu sprawdzenia uaktualnionej wersji
funkcji login() nadal mona wykorzysta skrypt testLoginTrans.php, przedstawiony w poprzedniej sekcji. Powinien on wygenerowa takie same jak poprzednio dane wyjciowe.
Trzeba koniecznie zwrci uwag na fakt, e chocia wykonanie polecenia UPDATE w wyzwalaczu zawsze bdzie koczyo si niepowodzeniem, sam wyzwalacz zostanie poprawnie skompilowany.
Teraz, po uruchomieniu skryptu testLoginTrans.php, powinny zosta wywietlone nastpujce
dane wyjciowe:
Uwierzytelnienie zakoczyo si niepowodzeniem
120
Rozdzia 4. Transakcje
Jak mona si przekona, proces uwierzytelniania zakoczy si niepowodzeniem. Aby upewni si, e wykonanie polecenia INSERT w tabeli logons rwnie bdzie miao taki wynik, mona
oblicza liczb rekordw tabeli przed i po wykonaniu skryptu testLoginTrans.php. Taki efekt
da si osign za pomoc poniszego polecenia SQL, ktre wydano z poziomu narzdzia
SQL*Plus po nawizaniu poczenia z baz danych jako usr/usr:
SELECT count(*) FROM logons;
Jednak powysze stwierdzenie nie zawsze jest prawdziwe. Na przykad wyzwalacz logons_insert
moe zosta zaimplementowany w taki sposb, e efekty dziaania polecenia INSERT nie zostan wycofane, kiedy wykonanie polecenia UPDATE z tego wyzwalacza zakoczy si niepowodzeniem. Warto przeanalizowa przedstawion poniej wersj wyzwalacza logons_insert:
CREATE OR REPLACE TRIGGER logons_insert
BEFORE INSERT
ON logons
FOR EACH ROW
BEGIN
UPDATE accounts
SET num_logons = num_logons + 'str'
WHERE usr_id = :new.usr_id;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
/
Teraz, po uruchomieniu skryptu testLoginTrans.php powinny zosta wywietlone dane wyjciowe o podobnej postaci:
Witaj Bob Robinson
Odwiedzie nas ju 3 raz(y)
Twoja nazwa konta: bob
Nastpnie jeeli skrypt zostanie uruchomiony ponownie, wywietlana liczba operacji logowania pozostanie taka sama. Jednak po sprawdzeniu liczby rekordw tabeli logons, jak przedstawiono we wczeniejszej czci podrozdziau, bdzie mona dostrzec, e kolejne wykonanie
skryptu testLoginTrans.php spowodowao zwikszenie wartoci tej liczby.
121
Wskazuje to, e chocia wykonanie polecenia UPDATE z wyzwalacza zakoczyo si niepowodzeniem, wykonanie polecenia INSERT zakoczyo si powodzeniem. Wynika to z faktu, e
omwiony powyej wyzwalacz logons_insert powoduje ciche zignorowanie jakiegokolwiek bdu
zgaszanego podczas jego wykonywania. W sekcji WHEN OTHERS ktra jest jedyn procedur
obsugi wyjtkw w czci wyzwalacza odpowiedzialnej za obsug wyjtkw ustalono
warto NULL.
W wikszoci przypadkw zastosowanie powyej techniki nie jest zalecane, poniewa powoduje zmian
oczekiwanego sposobu zachowania bazy danych. Rozsdne zaoenie jest takie, e jeli podczas wykonywania jakiegokolwiek polecenia SQL wystpi bd, modyfikacje wprowadzone przez to polecenie s
automatycznie wycofywane.
Dlatego te zamiast ustawia warto NULL w procedurze obsugi wyjtkw, naley utworzy
kod, ktry bdzie podejmowa odpowiednie dziaania w odpowiedzi na wystpienie bdu.
Na przykad mona wykorzysta procedur RAISE_APPLICATION_ERROR w celu wygenerowania
zdefiniowanego przez uytkownika bdu ORA. Znajdujcy si poniej fragment kodu pokazuje, w jaki sposb wyzwalacz logons_insert mgby zosta zmodyfikowany, aby wywoywa
RAISE_APPLICATION_ERROR z poziomu procedury obsugi bdw:
CREATE OR REPLACE TRIGGER logons_insert
BEFORE INSERT
ON logons
FOR EACH ROW
BEGIN
UPDATE accounts
SET num_logons = num_logons + 'str'
WHERE usr_id = :new.usr_id;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20000, 'Uaktualnienie licznika nie powiodo si.');
END;
/
122
Rozdzia 4. Transakcje
Liczba rekordw w tabeli logons powinna pozosta taka sama, co oznacza, e niepowodzenie
operacji uaktualnienia tabeli accounts przez wyzwalacz powoduje nie tylko wycofanie zmian
wprowadzonych przez polecenie UPDATE, ale rwnie przez polecenie INSERT.
Przed zakoczeniem pracy z tym przykadem naley si upewni o ponownym utworzeniu
wyzwalacza logons_trigger, aby klauzula SET w jego poleceniu UPDATE przedstawiaa si nastpujco:
SET num_logons = num_logons + 1
123
124
Rozdzia 4. Transakcje
if (!oci_execute($stmt, OCI_DEFAULT)) {
$err = oci_error($stmt);
trigger_error('Wykonanie zapytania zakoczyo si niepowodzeniem: '
. $err['message'], E_USER_ERROR);
};
print "<h2>Testowanie izolacji transakcji!</h2>";
print "<h4>Transakcja A w ramach poczenia conn1:</h4>";
print "<p>(wyniki po uaktualnieniu oraz przed zatwierdzeniem transakcji
w ramach poczenia conn1)</p>";
select_emp_job($conn1, $jobno);
print "<h4>Transakcja B w ramach poczenia conn2:</h4>";
print "<p>(wyniki po uaktualnieniu oraz przed zatwierdzeniem transakcji
w ramach poczenia conn1)</p>";
select_emp_job($conn2, $jobno);
if (!oci_commit($conn1)) {
$err = oci_error($conn1);
trigger_error('Zatwierdzenie transakcji zakoczyo si niepowodzeniem: '
.$err['message'], E_USER_ERROR);
}
print "<h4>Transakcja B w ramach poczenia conn2:</h4>";
print "<p>( wyniki po uaktualnieniu oraz po zatwierdzeniu transakcji
w ramach poczenia conn1)</p>";
select_emp_job($conn2, $jobno);
$query = "UPDATE employees SET salary = 17000 WHERE job_id=:jobid";
$stmt = oci_parse($conn1,$query);
oci_bind_by_name($stmt, ':jobid', $jobno);
if (!oci_execute($stmt)) {
$err = oci_error($stmt);
trigger_error('Wykonanie zapytania zakoczyo si niepowodzeniem: '
. $err['message'], E_USER_ERROR);
};
?>
Na rysunku 4.4 pokazano dane wyjciowe powyszego skryptu. Jak mona zobaczy, zmiany
wprowadzone przez operacj UPDATE przeprowadzon w transakcji dziaajcej w ramach poczenia conn1 z baz danych s widoczne wewntrz tej transakcji natychmiast po wykonaniu
polecenia UPDATE. Nie s natomiast widoczne dla wspbienej transakcji dziaajcej w ramach
poczenia conn2 a do chwili zatwierdzenia pierwszej transakcji.
Powracajc do kodu rdowego omawianego skryptu newConns.php: warto zwrci uwag,
e wszystkie polecenia SQL uyte w skrypcie zostay wykonane w trybie OCI_DEFAULT. Gwarantuje on natychmiastowe zatwierdzenie transakcji.
Naley rwnie zwrci uwag, e pierwsze poczenie w skrypcie zostao nawizane za pomoc funkcji oci_connect(). Poniewa jest to pierwsze poczenie, bufor pocze przypisany
temu skryptowi jest pusty, wic funkcja oci_connect() nawie nowe poczenie z baz danych.
125
Nastpnie w celu utworzenia nowego, transakcyjnie izolowanego poczenia w skrypcie nastpuje wywoanie funkcji oci_new_connect().
Poprzez wykonanie polecenia UPDATE w trybie OCI_DEFAULT w ramach poczenia conn1 nastpuje utworzenie transakcji w tym poczeniu.
Po wykonaniu polecenia UPDATE efekty tej operacji bd widoczne dla innych operacji przeprowadzanych wewntrz tej samej transakcji. Aby to udowodni, w skrypcie wywietlane s
rekordy zmodyfikowane przez polecenie UPDATE wewntrz tej samej transakcji przed jej zatwierdzeniem. Jak wida na rysunku 4.4, polecenie SELECT zwraca nowe wartoci uaktualnionych rekordw.
126
Rozdzia 4. Transakcje
Jednak podczas przeprowadzania operacji SELECT we wspbienej transakcji nadal bd widoczne pocztkowe wartoci uaktualnionych rekordw. Wynika to z faktu, e wspbiene transakcje s izolowane od zmian wprowadzonych przez niezatwierdzone transakcje. Gdy transakcja zostanie zatwierdzona, wszystkie wprowadzone przez ni zmiany stan si widoczne dla
innych transakcji.
Wreszcie, za pomoc funkcji UPDATE nastpuje przywrcenie wartoci pocztkowych uaktualnionym rekordom.
Chocia Oracle dostarcza zestaw funkcji, ktre mog pomc osign wymienione powyej
cele, prawidowe wykorzystanie ich jest ju zadaniem programisty. Przedstawione poniej
podrozdziay koncentruj si na pewnych kwestiach zwizanych z wspbienym uaktualnianiem danych, ktre mog pojawi si w rodowisku wielodostpnym podczas nieprawidowego uywania transakcji.
Wstrzymanie wykonywania poniszego skryptu za pomoc funkcji sleep() pozwala na symulacj przeprowadzania operacji wymagajcej ogromnej iloci oblicze.
127
<?php
// Plik: updateSleep.php.
if(!$dbConn = oci_connect('hr', 'hr', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Nie mona nawiza poczenia z baz danych: '
. $err['message'], E_USER_ERROR);
};
$jobno = 'AD_VP';
$query = "
UPDATE employees
SET salary = salary*1.1
WHERE job_id=:jobid";
$stmt = oci_parse($dbConn,$query);
oci_bind_by_name($stmt, ':jobid', $jobno);
if (!oci_execute($stmt, OCI_DEFAULT)) {
$err = oci_error($stmt);
trigger_error('Wykonanie zapytania zakoczyo si niepowodzeniem: '
. $err['message'], E_USER_ERROR);
};
$updrows = oci_num_rows($stmt);
print 'Uaktualniono '.$updrows. ' rekord(w)'.'<br/>';
sleep(20);
oci_rollback($dbConn);
print 'Transakcja zostaa wycofana.';
?>
128
Rozdzia 4. Transakcje
Utracone uaktualnienia
Omwiony powyej przykad pokaza, jak kiepsko zaprojektowana aplikacja transakcyjna moe na dugo zablokowa zasoby bazy danych, co uniemoliwi wspbienym transakcjom uzyskanie dostpu do tych zasobw w rozsdnym czasie. Jednak warto zwrci uwag, e zastosowanie podejcia bez blokad podczas modyfikowania danych bazy danych w rodowisku
wielodostpnym moe spowodowa inny problem utracone uaktualnienia. Aby zrozumie,
na czym polega problem utraconych uaktualnie, naley przeanalizowa kroki, ktre interaktywna aplikacja zwykle przeprowadza podczas modyfikacji informacji przechowywanych
w bazie danych:
Q Wybr danych z bazy danych.
Q Wywietlenie danych uytkownikowi.
Q Oczekiwanie na dziaanie uytkownika.
Q Uaktualnienie danych w bazie danych.
Powinno by cakiem oczywiste, e kiedy w powyszym przykadzie aplikacja czeka na dziaanie uytkownika, inny uytkownik moe spowodowa zmian danych. Dlatego te jeeli
pierwszy uytkownik bdzie kontynuowa proces uaktualniania danych, zmiany wprowadzone
przez drugiego uytkownika zostan utracone.
Problem mona lepiej zrozumie na podstawie przykadu. Warto wic spojrze na skrypt
updateQuickForm.php, ktry implementuje powysze kroki za pomoc pakietu PEAR o nazwie
HTML_QuickForm. Po uruchomieniu skryptu wykona ona nastpujce kroki:
Q Uaktualni dwa rekordy w tabeli employees.
Q Wygeneruje formularz proszcy uytkownika o zatwierdzenie bd wycofanie zmian.
Q Zakoczy dziaanie, wycofujc wprowadzone zmiany.
Za pomoc formularza wygenerowanego przez skrypt uytkownik moe albo zatwierdzi, albo
wycofa zmiany, a nastpnie musi klikn przycisk Wylij. Gdy to zrobi, skrypt zostanie wywoany ponownie, tym razem wykonujc ponisze kroki:
Q Uaktualnienie tych samych rekordw w tabeli employees.
Q Zatwierdzenie lub wycofanie zmian w zalenoci od decyzji uytkownika.
Q Zakoczenie dziaania skryptu.
129
Po przeprowadzeniu powyszych krokw mona uruchomi skrypt updateQuickForm.php, ktrego kod rdowy przedstawiono poniej:
<?php
// Plik: updateQuickForm.php.
require_once 'HTML/QuickForm.php';
if(!$dbConn = oci_connect('hr', 'hr', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Nie mona nawiza poczenia z baz danych: '
. $err['message'], E_USER_ERROR);
};
$jobno = 'AD_VP';
$query = "
UPDATE employees
SET salary = salary*1.1
WHERE job_id=:jobid";
$stmt = oci_parse($dbConn,$query);
oci_bind_by_name($stmt, ':jobid', $jobno);
if (!oci_execute($stmt, OCI_DEFAULT)) {
$err = oci_error($stmt);
trigger_error('Wykonanie zapytania zakoczyo si niepowodzeniem: '
.$err['message'], E_USER_ERROR);
};
print '<h2>Potwierdzenie uaktualnienia!</h2>';
$updrows = oci_num_rows($stmt);
130
Rozdzia 4. Transakcje
131
Transakcje autonomiczne
Kontynuujc poprzedni przykad: moe wystpi potrzeba rejestrowania prb uaktualnienia
rekordw w tabeli employees. Aby umoliwi takie zadanie, trzeba utworzy tabel przechowujc
sprawdzane rekordy, jak rwnie wyzwalacz BEFORE UPDATE, ktry obejmuje tabel employees.
Zadaniem wyzwalacza bdzie wstawianie rekordu do nowej tabeli za kadym razem, gdy ktokolwiek sprbuje uaktualni rekord w tabeli employees.
W celu utworzenia wymienionej struktury danych naley wyda nastpujce polecenie SQL:
CONN usr/usr
CREATE TABLE emp_updates(
emp_id NUMBER(6),
job_id VARCHAR2(10),
timedate DATE);
CONN /AS SYSDBA
GRANT INSERT on usr.emp_updates TO hr;
CONN hr/hr
CREATE OR REPLACE TRIGGER emp_updates_trigger
BEFORE UPDATE
ON employees
FOR EACH ROW
BEGIN
INSERT INTO usr.emp_updates VALUES (:new.employee_id, :new.job_id,
SYSDATE);
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001, 'W wyzwalaczu wystpi bd.');
END;
/
Po wykonaniu powyszego polecenia mona uruchomi skrypt updateQuickForm.php, omwiony w poprzednim podrozdziale, i sprawdzi, czy mechanizm kontroli dziaa zgodnie z zaoeniami. W formularzu wygenerowanym przez skrypt naley zaznaczy opcj wycofaj, a nastpnie klikn przycisk Wylij. Teraz przy prbie wywietlenia rekordw tabeli emp_updates
w nastpujcy sposb:
CONN usr/usr;
SELECT * FROM emp_updates;
Czytelnik powinien zobaczy, e tabela emp_updates wci nie zawiera adnych rekordw:
no rows selected
Oznacza to, e po wycofaniu operacji UPDATE nastpio wycofanie rekordu take z tabeli kontrolnej. Takie zachowanie jest oczekiwane, poniewa nie mona wycofa pewnych efektw
transakcji mona albo zatwierdzi wszystkie efekty, albo wszystkie wycofa.
132
Rozdzia 4. Transakcje
Prba zatwierdzenia jedynie polecenia INSERT wykonywanego wewntrz wyzwalacza zakoczy si niepowodzeniem, poniewa w wyzwalaczu niedozwolone jest uycie adnych polece
nadzorujcych transakcj. Dlatego te po prbie utworzenia wyzwalacza emp_updates_trigger
w nastpujcy sposb (trzeba pamita, aby by poczonym jako uytkownik hr wykonujc polecenie CONN hr/hr inaczej tabela employees nie bdzie widoczna i nie uda si utworzy wyzwalacza):
CREATE OR REPLACE TRIGGER emp_updates_trigger
BEFORE UPDATE
ON employees
FOR EACH ROW
BEGIN
INSERT INTO usr.emp_updates VALUES (:new.employee_id,
:new.job_id, SYSDATE);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
/
133
Warto zwrci uwag, e chocia prba dotyczya uaktualnienia tylko dwch rekordw tabeli
employees, do tabeli kontrolnej emp_updates zostay wstawione cztery rekordy. Trzeba pamita, e w rzeczywistoci skrypt updateQuickForm.php dwukrotnie przeprowadza operacj
UPDATE. Po raz pierwszy w celu obliczenia liczby rekordw przeznaczonych do uaktualnienia.
Natomiast druga operacja faktycznie uaktualnia te rekordy.
134
Rozdzia 4. Transakcje
Podsumowanie
Niektre operacje wykonywane wzgldem bazy danych maj sens jedynie po zgrupowaniu
ich. Klasycznym przykadem jest operacja przelewu rodkw pieninych midzy dwoma
kontami bankowymi. Jedynym sposobem bezpiecznego przeprowadzenia tego rodzaju operacji pozostaje uycie transakcji. Zastosowanie transakcji pozwala na zgrupowanie polece SQL
w logiczne, niewidoczne jednostki pracy, z ktrych kada moe by albo w caoci zatwierdzona, albo w caoci wycofana.
W rozdziale Czytelnik dowiedzia si, kiedy i jak wykorzystywa transakcje w aplikacjach
PHP/Oracle. Analiza rozpocza si od oglnego przedstawienia transakcji Oracle oraz wyjanienia powodw, dla ktrych programista miaby ich uywa w aplikacjach PHP zbudowanych na Oracle. Nastpnie omwiono organizacj aplikacji PHP/Oracle w celu efektywnego
kontrolowania transakcji skoncentrowano si na korzyciach wynikajcych z przeniesienia
logiki biznesowej aplikacji transakcyjnej z PHP do bazy danych. Czytelnik dowiedzia si
rwnie, ktre funkcje rozszerzenia OCI8 suce do nawizywania poczenia naley wybiera
podczas uywania transakcji, a take jak tworzy wspbiene transakcje w ramach tego samego skryptu. Wreszcie zaprezentowano wywoywanie niezalenej transakcji z wewntrz
innej transakcji oraz przedstawiono sytuacje, w ktrych zastosowanie takiego rozwizania
moe by podane.
135