PHP i Oracle Tworzenie aplikacji webowych od przetwarzania danych po Ajaksa


PHP i Oracle. Tworzenie
aplikacji webowych:
od przetwarzania danych
po Ajaksa
Autor: Yuli Vasiliev
Tłumaczenie: Robert Górczyński, Artur Przybyła
ISBN: 978-83-246-1974-0
Tytuł oryginału: PHP Oracle Web Development:
Data processing, Security, Caching, XML,
Web Services, and Ajax
Format: 170x230, stron: 392
Poznaj niezwykłe możliwoSci duetu Oracle PHP i twórz niezawodne aplikacje!
" Jak połączyć PHP i Oracle w celu uzyskania optymalnej wydajnoSci
i niezawodnoSci?
" Jak wykorzystywać funkcje XML w PHP i Oracle?
" Jak poprawić wydajnoSć dzięki zastosowaniu buforowania?
Baza Danych Oracle nie ma sobie równych pod względem wydajnoSci, niezawodnoSci
oraz skalowalnoSci. Natomiast skryptowy język PHP dzięki niezwykłej prostocie
stosowania stanowi jedno z najpopularniejszych narzędzi budowania aplikacji
sieciowych  nawet dla niezbyt doSwiadczonych programistów. Budowanie i wdrażanie
aplikacji PHP opartych na Oracle pozwala więc na optymalne połączenie potężnych
możliwoSci i solidnoSci z łatwoScią użycia i krótkim czasem programowania.
Książka  PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych
po Ajaksa zawiera zilustrowany praktycznymi przykładami opis technologii oraz
wszystkich narzędzi potrzebnych, aby optymalnie wykorzystać możliwoSci duetu
Oracle PHP. Dzięki temu podręcznikowi poznasz nowe funkcje PHP i bazy danych
Oracle; dowiesz się także, na czym polega programowanie procedur składowanych
i obsługa transakcji. Nauczysz się tworzyć niezawodne aplikacje i zapewniać im wyższą
wydajnoSć dzięki mechanizmom buforowania, a także używać technologii Ajax
z technologiami Oracle Database i funkcjami PHP w celu usprawnienia reakcji aplikacji
na działania użytkownika.
" Połączenie PHP i Oracle
" Przetwarzanie danych
" Tworzenie i wywoływanie wyzwalaczy
" Używanie podprogramów składowanych
" PodejScie zorientowane obiektowo
" Obsługa wyjątków
" Bezpieczeństwo
" Buforowanie
" Aplikacje oparte na XML
" Usługi sieciowe
" Aplikacje oparte na Ajaksie
Połącz wydajnoSć, skalowalnoSć i niezawodnoSć z łatwoScią użycia
i krótkim czasem programowania!
Spis treści
O autorze 11
O recenzencie 13
Wprowadzenie 15
Jakie tematy zostały poruszone w książce? 16
Dla kogo przeznaczona jest ta książka? 17
Konwencje zastosowane w książce 17
Użycie przykładowych kodów 18
Rozdział 1. Rozpoczęcie pracy z PHP i Oracle 19
Dlaczego PHP i Oracle? 20
Prostota i elastyczność 20
Wydajność 21
Niezawodność 21
Co zamiast PHP i Oracle? 22
PHP i MySQL 22
JSF i Oracle 23
Co będzie potrzebne, aby rozpocząć pracę?23
Wymagane komponenty oprogramowania 23
Rozważania dotyczące produktu Oracle Database 25
Zrozumienie Oracle Database 25
Wybór między wydaniami oprogramowania Oracle Database 25
Pobieranie oprogramowania Oracle Database 26
Rozważania dotyczące PHP 27
Serwer WWW Apache 28
Dlaczego PHP 5? 28
Pobieranie PHP 5 29
Zmuszenie PHP i Oracle do współpracy 30
Oracle Instant Client 30
Zend Core for Oracle 31
Spis treści
Używanie Oracle SQL*Plus 31
Dlaczego warto używać SQL*Plus w programowaniu PHP/Oracle? 31
Nawiązywanie połączenia z bazą danych za pomocą SQL*Plus 32
Wykonywanie skryptów z poziomu SQL*Plus 34
Połączenie wszystkiego razem 35
Utworzenie pierwszej aplikacji PHP/Oracle 37
Nawiązywanie połączenia z bazą danych 40
Używanie metody Local Naming 40
Używanie metody Easy Connect 41
Wykonywanie poleceń SQL względem bazy danych 42
Pobieranie i wyświetlanie wyników 42
Podsumowanie 43
Rozdział 2. Połączenie PHP i Oracle 45
Przedstawienie rozszerzenia PHP OCI8 45
Dlaczego warto używać rozszerzenia OCI8? 46
Przetwarzanie poleceń za pomocą rozszerzenia OCI8 46
Nawiązywanie połączenia z Oracle za pomocą rozszerzenia OCI8 51
Definiowanie ciągu tekstowego połączenia 51
Funkcje rozszerzenia OCI8, które służą do nawiązywania połączenia z Oracle 52
Analizowanie i wykonywanie poleceń SQL za pomocą rozszerzenia OCI8 53
Przygotowywanie poleceń SQL do wykonania 54
Używanie zmiennych wiązanych 54
Wykonywanie poleceń SQL 56
Obsługa błędów 56
Używanie funkcji oci_error() 57
Używanie funkcji trigger_error() 57
Używanie wyjątków 58
Pobieranie wyników za pomocą funkcji rozszerzenia OCI8 59
Funkcje rozszerzenia OCI8, które służą do pobierania wyników 59
Pobieranie kolejnego rekordu 60
Pobranie wszystkich rekordów 61
Alternatywy dla rozszerzenia PHP OCI8 63
Używanie PEAR DB 63
Używanie ADOdb 65
Używanie PDO 66
Tworzenie własnej biblioteki na bazie rozszerzenia OCI8 67
Podsumowanie 68
Rozdział 3. Przetwarzanie danych 71
Implementacja logiki biznesowej aplikacji PHP/Oracle 72
Kiedy przenosić dane do miejsca działania procesu przetwarzania? 72
Zalety przeniesienia procesu przetwarzania danych do samych danych 73
Sposoby implementacji logiki biznesowej wewnątrz bazy danych 74
Współpraca między komponentami implementującymi logikę biznesową 75
4
Spis treści
Używanie skomplikowanych poleceń SQL 76
Używanie funkcji Oracle SQL w zapytaniach 76
Funkcje Oracle SQL kontra przetwarzanie danych w PHP 77
Funkcje agregujące 79
Klauzula GROUP BY 80
Używanie złączeń 80
Wykorzystanie zalet widoków 83
Kluczowe korzyści płynące z używania widoków 83
Ukrywanie złożoności danych za pomocą widoków 84
Używanie klauzuli WHERE 85
Używanie podprogramów składowanych 87
Czym są podprogramy składowane? 87
Zalety podprogramów składowanych 89
Przykład użycia podprogramu składowanego 90
Tworzenie podprogramów składowanych 94
Wywoływanie podprogramów składowanych z poziomu PHP 95
Używanie wyzwalaczy 97
Tworzenie wyzwalaczy 98
Wywoływanie wyzwalaczy 99
Wywoływanie procedur składowanych z poziomu wyzwalacza 99
Podsumowanie 100
Rozdział 4. Transakcje 103
Ogólny opis transakcji 104
Czym jest transakcja? 104
Czym są reguły ACID? 105
W jaki sposób transakcje działają w Oracle? 106
Używanie transakcji w aplikacjach PHP/Oracle 107
Strukturyzacja aplikacji PHP/Oracle w celu nadzorowania transakcji 110
Tworzenie kodu transakcyjnego 113
Nadzorowanie transakcji z poziomu PHP 113
Przenoszenie kodu transakcyjnego do bazy danych 119
Używanie wyzwalaczy 119
Wycofanie na poziomie polecenia 120
Rozważania dotyczące izolacji transakcji 123
Którą funkcję rozszerzenia OCI8 służącą do nawiązywania połączenia należy wybrać? 123
Kwestie związane z współbieżnym uaktualnianiem 127
Kwestie związane z nakładaniem blokad 127
Utracone uaktualnienia 129
Transakcje autonomiczne 132
Podsumowanie 135
Rozdział 5. Podejście zorientowane obiektowo 137
Implementacja klas PHP, które pozwalają na współpracę z Oracle 138
Bloki budulcowe aplikacji 138
Tworzenie zupełnie od początku własnej klasy PHP 139
Testowanie nowo utworzonej klasy 141
Wykorzystanie zalet funkcji programowania zorientowanego obiektowo w PHP 5 142
5
Spis treści
Funkcjonalność i implementacja 144
Ponowne używanie kodu 146
Obsługa wyjątków 146
Modyfikacja istniejącej klasy w celu użycia wyjątków 147
Rozróżnienie między odmiennymi rodzajami błędów 149
Czy wyjątki koniecznie oznaczają błędy? 152
Rozszerzanie istniejących klas 152
Używanie klas standardowych 152
Pakiet PEAR::Auth w działaniu 153
Zabezpieczanie stron za pomocą PEAR::Auth 155
Dostosowanie klas standardowych do własnych potrzeb 157
Dostosowanie do własnych potrzeb PEAR::Auth 157
Budowanie mniejszego kodu klienta 160
Oddziaływania między obiektami 161
Kompozycja 161
Agregacja 164
Komunikacja bazująca na zdarzeniach 168
Używanie właściwości obiektowych Oracle 170
Używanie typów obiektowych w Oracle 170
Implementacja logiki biznesowej za pomocą metod obiektów Oracle 171
Używanie obiektów Oracle w celu uproszczenia tworzenia aplikacji 174
Podsumowanie 175
Rozdział 6. Bezpieczeństwo 177
Zabezpieczanie aplikacji PHP/Oracle 178
Uwierzytelnianie użytkowników 178
Oddzielenie zarządzania bezpieczeństwem od danych 179
Używanie dwóch schematów bazy danych w celu zwiększenia bezpieczeństwa 180
Używanie trzech schematów bazy danych w celu zwiększenia bezpieczeństwa 182
Używanie pakietów PL/SQL i funkcji tabelarycznych
w celu zapewnienia bezpiecznego dostępu do danych bazy danych 183
Używanie atrybutu %ROWTYPE 187
Budowanie własnego magazynu dla klasy PEAR::Auth 189
Testowanie systemu uwierzytelniania 190
Przeprowadzanie uwierzytelniania na podstawie tożsamości użytkownika 192
Używanie sesji do przechowywania informacji o uwierzytelnionym użytkownika 192
Przechowywanie informacji o użytkowniku w zmiennych pakietowych 193
Ochrona zasobów na podstawie informacji dotyczących uwierzytelnionego użytkownika 195
Używanie skrótów 199
Tworzenie skrótów haseł 200
Modyfikacja systemu uwierzytelniania
w celu przeprowadzenia operacji tworzenia skrótu 202
Implementacja dokładnej kontroli dostępu za pomocą widoków bazy danych 204
Implementacja bezpieczeństwa na poziomie kolumny za pomocą widoków 205
Maskowanie wartości kolumn zwracanych aplikacji 208
Używanie funkcji DECODE() 208
Implementacja bezpieczeństwa na poziomie rekordu za pomocą widoków 211
Bezpieczeństwo na poziomie rekordu przy użyciu funkcji VPD 214
Podsumowanie 217
6
Spis treści
Rozdział 7. Buforowanie 219
Buforowanie danych za pomocą Oracle i PHP 220
Buforowanie zapytań w serwerze bazy danych 220
Przetwarzanie poleceń SQL 220
Stosowanie zmiennych wiązanych
w celu zwiększenia prawdopodobieństwa użycia bufora puli współdzielonej 222
Używanie kontekstu Oracle podczas buforowania 224
Tworzenie kontekstu globalnego aplikacji 226
Manipulowanie danymi znajdującymi się w kontekście globalnym 228
Zerowanie wartości w kontekście globalnym 232
Mechanizmy buforowania dostępne w PHP 236
Wybór strategii buforowania 236
Wywoływanie funkcji buforowania za pomocą pakietu PEAR::Cache_Lite 237
Uaktualnianie buforowanych danych 240
Implementacja buforowania bazującego na powiadamianiu 242
Używanie funkcji bazy danych powiadamiania o zmianach 244
Kontrola komunikatów powiadamiania 244
Budowanie procedury PL/SQL, która wysyła powiadomienia serwerowi WWW 245
Przeprowadzenie kroków konfiguracyjnych wymaganych
przez mechanizm powiadamiania 246
Budowa uchwytu powiadamiania 247
Utworzenie zapytania rejestrującego dla uchwytu powiadamiania 249
Szybki test 250
Implementacja buforowania bazującego na powiadomieniach
za pomocą PEAR::Cache_Lite 251
Podsumowanie 253
Rozdział 8. Aplikacje bazujące na XML-u 255
Przetwarzanie danych XML w aplikacjach PHP/Oracle 256
Przetwarzanie danych XML za pomocą PHP 256
Tworzenie danych XML za pomocą rozszerzenia PHP DOM 257
Wykonywanie zapytań do dokumentu DOM za pomocą XPath 259
Transformacja i przetwarzanie danych XML za pomocą XSLT 260
Wykonywanie przetwarzania danych XML wewnątrz bazy danych 265
Używanie funkcji generowania SQL/XML w Oracle 265
Przeniesienie całego procesu przetwarzania danych XML do bazy danych 268
Przechowywanie danych XML w bazie danych 269
Przeprowadzanie transformacji XSLT wewnątrz bazy danych 271
Budowanie aplikacji PHP na podstawie Oracle XML DB 272
Używanie bazy danych Oracle do przechowywania,
modyfikowania i pobierania danych XML 273
Dostępne opcje przechowywania danych XML w bazie danych Oracle 273
Używanie XMLType do obsługi danych XML w bazie danych 275
Używanie schematów XML 277
Pobieranie danych XML 281
Uzyskanie dostępu do danych relacyjnych za pomocą widoków XMLType 285
Używanie widoków XMLType 285
Tworzenie widoków XMLType bazujących na schemacie XML 286
Przeprowadzanie operacji DML w widoku XMLType bazującym na schemacie XML 289
7
Spis treści
Używanie repozytorium Oracle XML DB 293
Manipulowanie zasobami repozytorium za pomocą kodu PL/SQL 294
Uzyskanie dostępu do zasobów repozytorium za pomocą SQL 294
Wykorzystanie zalet standardowych protokołów internetowych 295
Obsługa transakcji 297
Pobieranie danych za pomocą Oracle XQuery 298
Używanie silnika XQuery do budowania danych XML
na podstawie danych relacyjnych 299
Rozłożenie danych XML na postać danych relacyjnych 301
Podsumowanie 302
Rozdział 9. Usługi sieciowe 303
Udostępnienie aplikacji PHP/Oracle jako usługi sieciowej za pomocą rozszerzenia PHP SOAP 304
Komunikacja za pomocą SOAP 304
Co jest wymagane do zbudowania usługi sieciowej SOAP? 305
Budowanie usługi sieciowej SOAP na podstawie aplikacji PHP/Oracle 307
Budowanie logiki biznesowej usługi sieciowej wewnątrz bazy danych 308
Tworzenie schematu XML przeznaczonego do weryfikacji nadchodzących dokumentów 308
Generowanie unikalnych identyfikatorów dla przekazywanych dokumentów 311
Tworzenie podprogramów PL/SQL implementujących logikę biznesową usługi sieciowej 313
Budowanie uchwytu klasy PHP 317
Używanie WSDL 319
Tworzenie serwera SOAP za pomocą rozszerzenia PHP SOAP 322
Budowanie klienta SOAP w celu przetestowania serwera SOAP 323
Bezpieczeństwo 326
Implementacja logiki autoryzacji wewnątrz bazy danych 327
Tworzenie uchwytu klasy PHP 329
Tworzenie dokumentu WSDL 330
Tworzenie skryptu klienta 332
Podsumowanie 333
Rozdział 10. Aplikacje oparte na Ajaksie 335
Budowanie aplikacji PHP/Oracle opartych na Ajaksie 336
Ajax  zasada działania 336
Projekt aplikacji monitorującej opartej na Ajaksie/PHP/Oracle 337
Rozwiązanie oparte na Ajaksie 339
Tworzenie struktur danych 339
Tworzenie skryptu PHP przetwarzającego żądania Ajaksa 340
Używanie obiektu JavaScript  XMLHttpRequest 341
Złożenie aplikacji w całość 345
Użycie pamięci podręcznej w celu zwiększenia szybkości pracy aplikacji 347
Implementacja rozwiązań Master/Detail z użyciem metodologii Ajax 348
Projektowanie rozwiązania Master/Detail wykorzystującego Ajaksa 348
Opis działania przykładowej aplikacji 349
Tworzenie struktur danych 351
Generowanie kodu HTML za pomocą Oracle XQuery 353
Wysyłanie żądań POST za pomocą Ajaksa 354
Tworzenie stylów CSS 356
Złożenie aplikacji w całość 357
Podsumowanie 358
8
Spis treści
Dodatek A Instalacja oprogramowania PHP i Oracle 359
Instalacja oprogramowania Oracle Database 360
Instalacja wydań Oracle Database Enterprise/Standard 360
Instalacja wydania Oracle Database Express Edition 363
Instalacja wydania Oracle Database XE w systemie Windows 363
Instalacja wydania Oracle Database XE w systemie Linux 365
Instalacja serwera WWW Apache 365
Instalacja PHP 367
Instalacja PHP w systemie Windows 367
Instalacja PHP w systemie z rodziny Unix 368
Testowanie PHP 369
Zbudowanie mostu między Oracle i PHP 369
Biblioteki Oracle Instant Client 369
Włączenie rozszerzenia OCI8 w istniejącej instalacji PHP 371
Instalacja narzędzia SQL*Plus Instant Client 372
Instalacja Zend Core for Oracle 373
Instalacja Zend Core for Oracle w systemie Windows 373
Instalacja Zend Core for Oracle w systemie Linux 374
Skorowidz 375
9
4
Transakcje
Aby uzyskać pewność, że używane dane zawsze będą prawidłowe, należy stosować transakcje.
W skrócie: dostarczają one mechanizm pozwalający na bezpieczne modyfikowanie danych prze-
chowywanych w bazie danych poprzez przeniesienie bazy danych z jednego spójnego stanu
do kolejnego.
Klasycznym przykładem wykorzystania transakcji jest operacja bankowa, taka jak przelew
środków pieniężnych z jednego konta bankowego na inne. Załóżmy, że zachodzi potrzeba
przelania środków pieniężnych z konta oszczędnościowego na konto bieżące. W celu wyko-
nania tej operacji trzeba będzie przeprowadzić przynajmniej dwa kroki: zmniejszyć wartości
środków na koncie oszczędnościowym i zwiększyć wartość środków na koncie bieżącym.
Oczywiste jest, że w tego rodzaju sytuacji konieczne będzie potraktowanie obu operacji jako
pojedynczej, aby zachować saldo między kontami. Dlatego też żadna z wymienionych operacji nie
może zostać przeprowadzona oddzielnie  muszą być zakończone obie lub żadna z nich 
programista musi zagwarantować, że albo obie operacje zakończą się powodzeniem, albo
żadna z nich nie będzie przeprowadzona. W takiej sytuacji doskonałym rozwiązaniem jest za-
stosowanie transakcji.
W rozdziale zostały omówione różne mechanizmy, które mogą być użyte do przeprowadzania
transakcji za pomocą technologii PHP i Oracle. Zaczniemy od ogólnego omówienia transakcji,
ponieważ te informacje są bardzo ważne w celu dokładnego zrozumienia sposobu działania
transakcji. Następnie zostały przedstawione szczegółowy dotyczące stosowania na różne spo-
soby transakcji w aplikacjach PHP/Oracle.
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
Ogólny opis transakcji
Przed rozpoczęciem budowania własnych aplikacji PHP/Oracle wykorzystujących transakcje
należy zapoznać się z podstawowymi informacjami, które dotyczą transakcji, i przekonać się,
jak mogą być przeprowadzane z poziomu PHP i Oracle. W podrozdziale zaprezentowano
ogólny opis transakcji oraz poruszono następujące zagadnienia:
Czym są transakcje i kiedy programista może chcieć je stosować?
Jak przeprowadzać transakcje za pomocą PHP i Oracle?
Jak zorganizować aplikację PHP/Oracle, aby efektywnie kontrolować transakcje?
Ponieważ wymienione powyżej zagadnienia najlepiej można zrozumieć za pomocą przykła-
dów, w podrozdziale znajdzie się kilka prostych przykładów pokazujących możliwy sposób
wykorzystania transakcji w aplikacjach PHP/Oracle.
Czym jest transakcja?
Ogólnie rzecz biorąc, transakcja jest czynnością lub serią czynności, które przenoszą system
z jednego stanu spójności do kolejnego. Z punktu widzenia programisty budującego aplikacje
oparte na bazie danych transakcja może być uznawana za niewidzialny zestaw operacji, które
przenoszą bazę danych z jednego stanu spójności do kolejnego.
Transakcja jest jednostką logiczną pracy, zawierającą jedno lub większą liczbę poleceń SQL, które mogą
być w całości albo zatwierdzone, albo wycofane.
Oznacza to, że wszystkie polecenia SQL zawarte w transakcji muszą być z powodzeniem za-
kończone, aby cała transakcja mogła zostać zatwierdzona, dzięki czemu zmiany przeprowa-
dzone przez wszystkie operacje DML zostają trwale przeprowadzone. Graficznie zostało to
pokazane na rysunku 4.1.
Rysunek 4.1. Graficzne przedstawienie sposobu działania transakcji
104
Rozdział 4. " Transakcje
Jak można zobaczyć na powyższym rysunku, polecenia SQL składające się na transakcję przeno-
szą dane, na których operują z jednego stanu spójności do kolejnego. Transakcja musi zostać
zatwierdzona, aby wprowadzone przez nią zmiany zostały zastosowane w bazie danych i tym
samym przeniosły dane do kolejnego stanu spójności. W przeciwnym razie wszystkie polece-
nia SQL wykonane przez transakcję zostaną wycofane, a dane pozostaną w stanie, w którym
znajdowały się w chwili rozpoczęcia transakcji.
Jeżeli w trakcie wykonywania transakcji wystąpi błąd serwera, na przykład awaria sprzętu
komputerowego, efekty transakcji zostaną automatycznie wycofane. Jednak w pewnych sytu-
acjach programista może chcieć ręcznie wycofać ukończoną (ale jeszcze nie zatwierdzoną)
transakcję, w zależności od ustalonego warunku. Taka sytuacja została pokazana graficznie na
rysunku 4.2.
Rysunek 4.2. Graficzne przedstawienie warunkowego zatwierdzenia transakcji
Jak widać na powyższym rysunku, po zakończeniu wykonywania wszystkich poleceń transak-
cji programista ma możliwość albo jej zatwierdzenia, albo wycofania.
Czym są reguły ACID?
ACID to akronim oznaczający Atomicity (niepodzielność), Consistency (spójność), Isolation
(izolacja) oraz Durability (trwałość). Każdy system zarządzania bazą danych obsługujący transakcje
musi spełniać powyższe cechy charakterystyczne. Ich podsumowanie znalazło się w pierwszej
tabeli na następnej stronie.
Baza danych Oracle obsługuje wszystkie właściwości ACID wymienione w powyższej tabeli.
Dlatego podczas budowania aplikacji transakcyjnych na Oracle nie trzeba projektować wła-
snych schematów gwarantujących spójność i integralność danych. Zamiast tego zawsze lep-
szym rozwiązaniem będzie wykorzystanie transakcji Oracle i zrzucenie obsługi tego rodzaju
problemów na bazę danych.
105
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
Właściwość Opis
Niepodzielność Transakcja stanowi niepodzielną jednostkę pracy. Oznacza to, że albo wszystkie operacje
w ramach transakcji zostaną wykonane, albo nie będzie wykonana żadna z nich.
Spójność Transakcja przenosi bazę danych z jednego stanu spójności do kolejnego. Oznacza to,
że podczas przeprowadzania transakcji nie mogą być złamane żadne czynniki wpływające
na spójność bazy danych. Jeżeli transakcja złamie jakiekolwiek reguły spójności,
zostanie wycofana.
Izolacja Do chwili zatwierdzenia transakcji zmiany wprowadzane przez operacje składające się
na transakcję nie powinny być widoczne dla innych, równocześnie przeprowadzanych
transakcji.
Trwałość Gdy transakcja zostanie zatwierdzona, wszystkie modyfikacje wprowadzone przez
transakcję staną się trwałe i widoczne dla innych transakcji. Trwałość gwarantuje,
że jeśli zatwierdzenie transakcji zakończy się powodzeniem, w przypadku awarii
systemu nie będą one wycofane.
W jaki sposób transakcje działają w Oracle?
W podrozdziale zostanie pokrótce przedstawiony ogólny opis działania transakcji w Oracle.
Szczegółowe informacje dotyczące sposobu działania transakcji w bazie danych Oracle można
znalezć w dokumentacji Oracle: w rozdziale  Transaction Management w podręczniku użyt-
kownika Oracle Database Concepts.
W Oracle transakcja nie rozpoczyna się jawnie, lecz niejawnie podczas uruchamiania pierw-
szego wykonywalnego polecenia SQL. Jednak istnieje kilka sytuacji powodujących zakończe-
nie transakcji. Przedstawiono w poniższej tabeli:
Sytuacja Opis
Wydanie Po wydaniu polecenia COMMIT następuje zakończenie wykonywania bieżącej transakcji.
polecenia COMMIT Polecenie powoduje, że zmiany wprowadzone przez polecenia SQL transakcji stają się
trwałe.
Wydanie Po wydaniu polecenia ROLLBACK następuje zakończenie wykonywania bieżącej
polecenia ROLLBACK transakcji. Polecenie to powoduje, że zmiany wprowadzone przez polecenia SQL
transakcji zostają wycofane.
Wydanie Jeżeli zostanie wydane polecenie DDL, Oracle w pierwszej kolejności zatwierdzi bieżącą
polecenia DDL transakcję, a następnie wykona i zatwierdzi polecenie DDL w nowej transakcji, która
składa się z pojedynczego polecenia.
Zamknięcie Kiedy połączenie zostanie zamknięte, Oracle automatycznie zatwierdzi bieżącą
połączenia transakcję w tym połączeniu.
Nieprawidłowe Jeżeli wykonywanie programu zostanie nieprawidłowo przerwane, Oracle
przerwanie automatycznie wycofa bieżącą transakcję.
wykonywania
programu
106
Rozdział 4. " Transakcje
Jak można się przekonać na podstawie powyższej tabeli, transakcja zawsze będzie albo zatwier-
dzona, albo wycofana, niezależnie od tego, czy zostanie wyraznie zatwierdzona, czy wycofana.
Warto jednak zwrócić uwagę, że zawsze dobrą praktyką jest wyrazne zatwierdzanie lub wy-
cofywanie transakcji zamiast polegania na zachowaniu domyślnym Oracle. W rzeczywistości
zachowanie domyślne aplikacji transakcyjnej może być różne w zależności od narzędzia sto-
sowanego przez aplikację do nawiązywania połączenia z bazą danych Oracle.
Na przykład jeżeli skrypt PHP współpracuje z bazą danych Oracle za pomocą rozszerzenia
OCI8, nie można liczyć na to, że aktywna transakcja w połączeniu zostanie automatycznie
zatwierdzona po zamknięciu tego połączenia. W takim przypadku po zamknięciu połączenia
lub zakończeniu wykonywania skryptu aktywna transakcja zostanie wycofana.
Natomiast jeżeli rozłączenie z bazą danych nastąpi po wydaniu polecenia DISCONNECT z poziomu
narzędzia SQL*Plus lub nastąpi połączenie z bazą jako inny użytkownik używający polecenia
CONNECT bądz sesja SQL*Plus zostanie zamknięta w wyniku wydania polecenia EXIT, aktywna
transakcja w połączeniu będzie zatwierdzona.
Aby uniknąć nieoczekiwanego zachowania w aplikacji, zawsze dobrze jest wyraznie zatwierdzać bądz
wycofywać transakcje, zamiast polegać na zachowaniu domyślnym aplikacji transakcyjnej.
Używanie transakcji w aplikacjach PHP/Oracle
Jak wspomniano w poprzednim podrozdziale, w Oracle można wyraznie albo zatwierdzić, albo
wycofać transakcję, używając polecenia odpowiednio COMMIT lub ROLLBACK. W celu wykonania po-
wyższych poleceń z poziomu kodu PHP nie trzeba używać funkcji oci_parse() i oci_execute(),
jak podczas wykonywania innych poleceń SQL, takich jak SELECT lub INSERT. Zamiast tego
używa się funkcji rozszerzenia OCI8 o nazwach oci_commit() i oci_rollback().
Przedstawiony poniżej skrypt PHP demonstruje sposób wyraznego zatwierdzenia lub wyco-
fania transakcji z poziomu kodu PHP podczas używania operacji DML. Zadaniem skryptu
jest próba uaktualnienia rekordów tabeli employees, reprezentujących pracowników, których
identyfikator stanowiska wynosi ST_MAN (menedżer magazynu). Uaktualnienie polega na
zwiększeniu wysokości pensji o 10%. Jeżeli uaktualnienie jednego lub większej liczby rekordów
zakończy się niepowodzeniem, cała transakcja zostanie wycofana, a uaktualnionym polom
pensji będą przywrócone ich wartości początkowe. Cały proces podsumowują poniższe kroki:
Krok 1.: wykonanie zapytania względem tabeli employees w celu uzyskania liczby
rekordów przedstawiających pracowników na wskazanym stanowisku (menedżerów
magazynu).
Krok 2.: rozpoczęcie transakcji i wykonanie operacji UPDATE względem tabeli
employees w celu przeprowadzenia próby zwiększenia pensji menedżerów
magazynu o 10%.
107
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
Krok 3.: wycofanie transakcji, jeśli liczba rekordów zmodyfikowanych przez
operację UPDATE będzie mniejsza niż liczba rekordów przedstawiających
menedżerów. W przeciwnym razie transakcja zostanie zatwierdzona.
Teraz warto zapoznać się z kodem zródłowym skryptu, aby przekonać się, jak powyższe kroki
mogą być zaimplementowane w PHP za pomocą funkcji rozszerzenia OCI8.
// Plik: trans.php.
if(!$dbConn = oci_connect('hr', 'hr', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Nie można nawiązać połączenia z bazą danych: '
. $err['message'], E_USER_ERROR);
};
$query = "SELECT count(*) num_rows FROM employees
WHERE job_id='ST_MAN'";
$stmt = oci_parse($dbConn,$query);
if (!oci_execute($stmt)) {
$err = oci_error($stmt);
trigger_error('Wykonanie zapytania zakończyło się niepowodzeniem: '
. $err['message'], E_USER_ERROR);
};
oci_fetch($stmt);
$numrows = oci_result($stmt, 'NUM_ROWS');
oci_free_statement($stmt);
$query = "UPDATE employees e
SET salary = salary*1.1
WHERE e.job_id='ST_MAN' AND salary*1.1
BETWEEN (SELECT min_salary FROM jobs j WHERE j.job_id=e.job_id)
AND (SELECT max_salary FROM jobs j WHERE j.job_id=e.job_id)";
$stmt = oci_parse($dbConn,$query);
if (!oci_execute($stmt, OCI_DEFAULT)) {
$err = oci_error($stmt);
trigger_error('Operacja uaktualnienia zakończyła się niepowodzeniem: '
. $err['message'], E_USER_ERROR);
}
$updrows = oci_num_rows($stmt);
print "Próba uaktualnienia ".$numrows." rekordów.
";
print "Udało się uaktualnić ".$updrows." rekordów.
";
if ($updrows<$numrows) {
if (!oci_rollback($dbConn)) {
$err = oci_error($dbConn);
trigger_error('Nie udało się wycofać transakcji: '
.$err['message'], E_USER_ERROR);
}
print "Transakcja została wycofana.";
} else {
if (!oci_commit($dbConn)) {
$err = oci_error($dbConn);
108
Rozdział 4. " Transakcje
trigger_error('Nie udało się zatwierdzić transakcji: '
.$err['message'], E_USER_ERROR);
}
print "Transakcja została zatwierdzona.";
}
?>
W powyższym skrypcie zdefiniowano zapytanie zwracające liczbę rekordów, które przedsta-
wiają pracowników zatrudnionych jako menedżerowie magazynu. W zapytaniu użyto funkcji
count() w celu obliczenia liczby rekordów spełniających kryteria zdefiniowane w klauzuli
WHERE zapytania. W omawianym przypadku wartością zwrotną funkcji count(*) jest liczba rekor-
dów reprezentujących pracowników, dla których wartość pola job_id wynosi ST_MAN.
W skrypcie liczba rekordów przedstawiających menedżerów magazynu jest pobierana z bufo-
ra wynikowego za pomocą połączenia funkcji oci_fetch() i oci_result(). Nie trzeba w tym
przypadku stosować pętli, ponieważ zapytanie zwraca tylko jeden rekord zawierający poje-
dyncze pole o nazwie num_rows.
Następnie wykonywane jest zapytanie uaktualniające kolumnę salary tabeli employees. Uak-
tualnienie polega na zwiększeniu pensji menedżerów magazynu o 10%. Zapytanie uaktualnia
wysokość pensji tylko wtedy, gdy nowa wysokość pensji będzie mieściła się między minimal-
ną i maksymalną wysokością pensji ustalonymi dla menedżerów magazynu i zdefiniowanymi
w tabeli jobs.
W omawianym przykładzie polecenie UPDATE jest wykonywane w trybie wykonywania OCI_
DEFAULT. W ten sposób następuje rozpoczęcie transakcji, która pozwala programiście w dalszej
części skryptu na wyrazne zatwierdzenie lub wycofanie zmian wprowadzonych przez polece-
nie UPDATE. Interesującą kwestią, na którą warto zwrócić uwagę, jest fakt, że domyślny tryb
wykonywania to OCI_COMMIT_ON_SUCCESS, w którym polecenie jest zatwierdzane automatycz-
nie, jeśli jego wykonywanie zakończy się powodzeniem.
Według dokumentacji Oracle aplikacja zawsze powinna wyraznie zatwierdzać bądz wycofywać transak-
cję przed zakończeniem działania programu. Jednak podczas używania 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, jeśli jego wykonanie zakończy się powodzeniem
(czyli w sytuacji podobnej do wyraznego zatwierdzenia natychmiast po wykonaniu polecenia). Jeżeli
błąd serwera uniemożliwi zakończenie powodzeniem wykonywania polecenia SQL, Oracle automatycznie
wycofa wszystkie zmiany wprowadzone przez nie.
W omawianym skrypcie funkcja oci_num_rows() jest wywoływana w celu uzyskania liczby re-
kordów zmodyfikowanych przez operację UPDATE. Po poznaniu liczby rekordów przedstawia-
jących menedżerów magazynu oraz liczby faktycznie uaktualnionych można porównać te
wartości i sprawdzić, czy są identyczne.
109
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
W powyższym skrypcie wycofanie transakcji następuje, gdy liczba uaktualnionych rekordów
jest mniejsza niż całkowita liczba rekordów przedstawiających menedżerów magazynu. Ma to
sens, ponieważ niedopuszczalna jest sytuacja, w której uaktualnione zostaną rekordy tylko
niektórych menedżerów magazynu.
Możliwość wycofania zmian w przypadku wystąpienia takiej sytuacji ma znaczenie krytyczne
 pozwala wówczas na użycie innego skryptu, który będzie mógł prawidłowo uaktualnić
wszystkie rekordy przedstawiające menedżerów magazynu. Na przykład w rzeczywistej sytu-
acji prawdopodobnie pożądanym rozwiązaniem będzie zwiększenie pensji menedżera maga-
zynu do maksymalnej dopuszczalnej wysokości, jeśli jej podwyżka o 10% przekroczy to do-
zwolone maksimum.
Jeżeli operacja UPDATE zmodyfikuje wszystkie rekordy przedstawiające menedżerów magazy-
nu, transakcję można zatwierdzić za pomocą funkcji oci_commit(), dzięki czemu wprowadzo-
ne zmiany będą trwałe.
Innym elementem, na który warto zwrócić uwagę w omawianym skrypcie, jest użyty mecha-
nizm obsługi błędów. Jeżeli w trakcie wykonywania funkcji oci_rollback() lub oci_commit()
wystąpi błąd, identyfikator połączenia będzie przekazany funkcji oci_error(), która z kolei
zwróci komunikat błędu opisujący powstały błąd.
Strukturyzacja aplikacji PHP/Oracle
w celu nadzorowania transakcji
Jak Czytelnik może przypomnieć sobie z lektury rozdziału 3., ogólnie rzecz biorąc, dobrym
pomysłem jest umieszczenie wewnątrz bazy danych kluczowej logiki biznesowej aplikacji
PHP/Oracle. Padło tam też również stwierdzenie, że w prostych przypadkach nie trzeba na-
wet tworzyć kodu PL/SQL w celu przeniesienia do bazy danych procesu przetwarzania da-
nych. Zamiast tego można zaprojektować skomplikowane zapytania SQL, które po wydaniu
będą nakazywały serwerowi bazy danych przeprowadzenie wszystkich wymaganych operacji
przetwarzania danych.
Wracając do przykładu omówionego w poprzednim podrozdziale: istnieje możliwość modyfi-
kacji użytego w nim polecenia UPDATE w taki sposób, aby rekordy przedstawiające menedże-
rów magazynu były uaktualniane tylko wtedy, gdy nowa wysokość pensji każdego menedżera
nadal będzie znajdowała się między wartością minimalną i maksymalną dla tego stanowiska.
Wymienione wartości są zdefiniowane w tabeli jobs. Taka modyfikacja wyeliminuje potrzebę
wykonywania oddzielnego zapytania, które zwraca liczbę rekordów spełniających powyższy
warunek. W ten sposób nastąpi zmniejszenie ilości kodu koniecznego do napisania w celu
implementacji żądanego zachowania funkcji.
W skrócie: nowa wersja polecenia UPDATE łączy w ramach pojedynczego polecenia wszystkie
trzy kroki wymienione na początku poprzedniego podrozdziału. Nie trzeba nawet wyraznie
110
Rozdział 4. " Transakcje
zatwierdzać bądz wycofywać operacji UPDATE. Zamiast tego polecenie UPDATE można wydać
w trybie OCI_COMMIT_ON_SUCCESS, który gwarantuje, że operacja zostanie automatycznie zatwier-
dzona, jeśli jej wykonanie zakończy się powodzeniem, a w przeciwnym razie  wycofana.
Przedstawiony poniżej skrypt prezentuje w działaniu nową wersję polecenia UPDATE:
// Plik: transQuery.php.
if(!$dbConn = oci_connect('hr', 'hr', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Nie można nawiązać połączenia 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 zakończyło się niepowodzeniem: '
. $err['message'], E_USER_ERROR);
};
$updrows = oci_num_rows($stmt);
if ($updrows>0) {
print "Transakcja została zatwierdzona.";
} else {
print "Transakcja została wycofana.";
}
?>
W powyższym fragmencie kodu zdefiniowano polecenie UPDATE, które spowoduje uaktualnie-
nie wszystkich rekordów reprezentujących menedżerów magazynu poprzez zwiększenie wy-
sokości ich pensji o 10%, pod warunkiem że żadna z nowych wartości pensji nie przekroczy
maksymalnej pensji menedżera magazynu zdefiniowanej w tabeli jobs. Jeżeli chociaż jedna
pensja przekroczy wartość maksymalną, polecenie UPDATE nie uaktualni żadnych rekordów.
Aby uzyskać taki sposób działania, zamiast podawania tabeli employees w klauzuli polecenia
UPDATE użyte zostało polecenie SELECT, którego wartością zwrotną są albo wszystkie rekordy
111
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
tabeli employees, albo żaden z nich. Zależy to od tego, czy wszystkie rekordy spełniające wa-
runek zdefiniowany w klauzuli WHERE polecenia UPDATE (w omawianym przypadku będą to
wszystkie rekordy reprezentujące menedżerów magazynu) mogą być uaktualnione w taki spo-
sób, aby każda nowa wysokość pensji nie przekraczała wartości maksymalnej pensji dla tego
stanowiska.
To polecenie SELECT jest uznawane za widok wewnętrzny. W przeciwieństwie do zwykłych widoków
omówionych w podrozdziale  Wykorzystanie zalet widoków , znajdującym się w rozdziale 3., widoki
wewnętrzne nie są obiektami schematu bazy danych, ale podzapytaniami, które mogą być stosowane
jedynie w ramach zawierających je poleceń za pomocą aliasów.
W omawianym przykładzie zastosowanie w poleceniu UPDATE wewnętrznego widoku emp po-
woduje wyeliminowanie potrzeby wykonywania oddzielnego zapytania zwracającego liczbę
rekordów reprezentujących menedżerów magazynu, a następnie sprawdzającego, czy otrzy-
mana liczba jest równa liczbie rekordów faktycznie zmodyfikowanych przez polecenie UPDATE.
Teraz skrypt wykonuje tylko pojedyncze zapytanie SQL, dzięki czemu wyraznie skraca czas
wykonywania skryptu.
Omówiony skrypt jest dobrym przykładem pokazującym korzyści, jakie można odnieść po przeniesieniu
kluczowej logiki biznesowej aplikacji PHP/Oracle z PHP do bazy danych Oracle. Tu zamiast używania
dwóch oddzielnych poleceń i analizowania ich wyników w PHP zastosowane zostało tylko jedno pole-
cenie SQL, które powoduje, że przetwarzanie danych zachodzi całkowicie w serwerze bazy danych.
Warto także zwrócić uwagę na użyty w skrypcie sposób wiązania zmiennych. Zmienna PHP
jobno jest dowiązywana do znacznika jobid użytego w poleceniu UPDATE. Interesujący jest fakt, że
znacznik zmiennej wiązanej jobid pojawia się w poleceniu częściej niż tylko jednokrotnie.
Inaczej niż w poprzednim przykładzie, w którym polecenie UPDATE było wykonywane w try-
bie OCI_DEFAULT, wyraznie rozpoczynającym transakcję, nowa wersja skryptu wykonuje pole-
cenie w trybie OCI_COMMIT_ON_SUCCESS. Operacja UPDATE jest więc automatycznie zatwierdza-
na, jeśli jej wykonanie zakończy się powodzeniem.
Jak wcześniej wspomniano, OCI_COMMIT_ON_SUCCESS jest trybem domyślnym wykonywania polece-
nia. Oznacza to, że nie trzeba wyraznie go określać podczas wywoływania funkcji oci_execute().
W omawianym przykładzie został wyraznie umieszczony w kodzie zródłowym, aby zwrócić na to uwagę.
W poprzednim przykładzie nadal używano funkcji oci_num_rows() w celu pobrania liczby re-
kordów zmodyfikowanych przez polecenie UPDATE. Jednak tym razem nie trzeba porównywać
tej liczby z całkowitą liczbą rekordów reprezentujących menedżerów magazynu, jak to miało
miejsce w przypadku poprzedniego skryptu. Wszystko, co trzeba zrobić, to prostu sprawdzić,
czy liczba rekordów zmodyfikowanych przez polecenie UPDATE jest większa niż 0.
112
Rozdział 4. " Transakcje
Jeżeli liczba uaktualnionych rekordów jest większa niż 0, oznacza to, że operacja UPDATE zmo-
dyfikowała wszystkie rekordy reprezentujące menedżerów magazynu i została z powodze-
niem zatwierdzona. W takim przypadku należy wyświetlić użytkownikowi komunikat infor-
mujący o zatwierdzeniu transakcji.
Jeżeli liczba uaktualnionych rekordów wynosi 0, oznacza to, że operacja UPDATE nie zmodyfi-
kowała żadnych rekordów. W takim przypadku należy wyświetlić użytkownikowi komunikat
informujący o wycofaniu transakcji. Jednak w rzeczywistości transakcja jest zatwierdzona, ale
żaden rekord nie został zmodyfikowany przez operację UPDATE.
Tworzenie kodu transakcyjnego
Dotychczas przedstawiono kilka prostych przykładów pokazujących podstawy działania
transakcji Oracle z PHP. W podrozdziale zostaną zaprezentowane bardziej skomplikowane
przykłady stosowania transakcji w aplikacjach PHP/Oracle.
Nadzorowanie transakcji z poziomu PHP
Jak Czytelnik dowiedział się z przykładów omówionych we wcześniejszej części rozdziału,
funkcja oci_execute() pozwala na wykonywanie polecenia SQL w dwóch trybach  OCI_COMMIT_
ON_SUCCESS oraz OCI_DEFAULT.
Podczas gdy stosowanie trybu OCI_COMMIT_ON_SUCCESS powoduje, że polecenia są automatycz-
nie zatwierdzane, użycie trybu OCI_DEFAULT wymaga wyraznego wywołania funkcji oci_commit()
w celu zatwierdzenia transakcji lub oci_rollback(), aby ją wycofać.
Jednak warto zwrócić uwagę, że gdy polecenie jest wykonywane w trybie OCI_DEFAULT, utwo-
rzona wówczas transakcja nadal może być zatwierdzona bez wywoływania funkcji oci_commit().
W tym celu pózniejsze polecenie należy wykonać w trybie OCI_COMMIT_ON_SUCCESS.
Powyższa technika może być zastosowana podczas grupowania dwóch lub większej liczby
poleceń w pojedynczą transakcję. Aby zagwarantować, że cała transakcja będzie wycofana,
kiedy wykonanie jednego z poleceń zakończy się niepowodzeniem lub zostaną uzyskane wy-
niki wskazujące na konieczność wycofania transakcji, można po prostu przerwać wykonywa-
nie skryptu. W tym celu można wywołać na przykład funkcję trigger_error() wraz ze stałą
E_USER_ERROR jako drugim parametrem, a tym samym wycofać transakcję bez potrzeby wy-
woływania funkcji oci_rollback().
Czytelnik może się zastanawiać, dlaczego omawiany jest niejawny sposób kończenia transakcji
Oracle z poziomu PHP zamiast wyraznego wywołania funkcji oci_commit() bądz oci_rollback().
W końcu druga z wymienionych funkcji stanowi zalecaną metodę kończenia transakcji. Ogólnie
113
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
rzecz biorąc, celem tej analizy jest umożliwienie łatwiejszego zrozumienia sposobu działania
transakcji Oracle w skryptach PHP, które współdziałają z bazą danych za pomocą rozsze-
rzenia OCI8.
W przykładzie, który zostanie omówiony poniżej, użyto struktur danych, które zdefiniowano
w podrozdziale  Przykład użycia podprogramu składowanego , znajdującym się w rozdziale 3.
Zanim jednak przejdziemy do przykładu, konieczne jest zmodyfikowanie wymienionych struktur
danych w przedstawiony poniżej sposób. Te polecenia SQL można wykonać z poziomu na-
rzędzia SQL*Plus po nawiązaniu połączenia 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 powyższym bloku kodu powoduje dodanie do tabeli accounts
kolumny num_logons typu INT. W nowej kolumnie będzie przechowywana liczba udanych operacji
logowania każdego użytkownika. W tym celu gdy uwierzytelnienie użytkownika zakończy się
powodzeniem, trzeba będzie zwiększyć liczbę operacji logowania przechowywaną w polu
num_logons.
Oczywiście, nadal można się obejść bez tej kolumny, wydając względem tabeli logons zapyta-
nie podobne do poniższego:
SELECT count(*) FROM logons WHERE usr_id='bob';
Jednak wraz ze wzrostem liczby operacji logowania powyższe zapytanie będzie bardzo kosz-
towne jak na otrzymanie informacji o liczbie logowań przeprowadzonych przez danego użyt-
kownika.
Po dodaniu komuny num_logons do tabeli accounts nowej kolumnie należy ustawić wartość
początkową wynoszącą 0. Ewentualnie wcześniejsze polecenie ALTER TABLE można było wydać
z użyciem klauzuli DEFAULT względem kolumny num_logons, na przykład następująco:
ALTER TABLE accounts
ADD (num_logons INT DEFAULT 0);
W omawianym bloku kodu następuje wyrazne zatwierdzenie transakcji, aby zmiany wprowa-
dzone przez operację UPDATE były trwałe.
W kolejnym kroku usuwane są wszystkie rekordy tabeli logons. Ten krok jest wymagany, aby
zagwarantować prawidłowe wykonanie polecenia check constraint zdefiniowanego w następnym
114
Rozdział 4. " Transakcje
kroku. W omawianym przykładzie można pominąć ten krok, jeżeli tabela logons nie zawiera
rekordów utworzonych w sobotę lub niedzielę, co umożliwi prawidłowe wykonanie polecenia
check constraint zdefiniowanego w kolejnym kroku. W przeciwnym razie podczas próby wyko-
nania polecenia ALTER TABLE zostanie wyświetlony następujący komunikat błędu:
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. Wymienio-
ne ograniczenie uniemożliwia wstawianie nowych rekordów do tabeli logons w sobotę lub
niedzielę. Pozwala to na modyfikację systemu uwierzytelniania w taki sposób, aby uniemożli-
wić użytkownikom logowanie się w soboty i niedziele, a tym samym pozwolić na logowanie
jedynie w dni robocze. W pózniejszym czasie takie ograniczenie zawsze będzie można usunąć
poprzez wydanie następującego polecenia:
ALTER TABLE logons DROP CONSTRAINT log_time_day;
Wróćmy jeszcze do polecenia ALTER TABLE, które przedstawiono powyżej. Warto zwrócić
uwagę na użyty 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.
Następnie stosowany jest operator NOT IN, który powoduje wykluczenie soboty (Saturday)
i niedzieli (Sunday) z listy dozwolonych dni.
Należy pamiętać, że baza danych Oracle rozróżnia wielkość liter podczas dopasowania. Dla-
tego też jeśli podano 'Day' jako drugi parametr funkcji TO_CHAR(), dni tygodnia na liście znajdują-
cej się po prawej stronie operatora NOT IN należy podać jako: 'Saturday', 'Sunday'. Miałyby one
postać 'SATURDAY', 'SUNDAY', gdyby drugi parametr funkcji TO_CHAR() został podany jako 'DAY'.
Powyższe polecenia powodują przeprowadzenie wszystkich wymaganych modyfikacji struk-
tury bazy danych. Po ich wykonaniu można więc przejść do przykładu, którego zadaniem jest zilu-
strowanie sposobów tworzenia transakcji poprzez wykonanie polecenia DML w trybie OCI_DEFAULT,
a następnie wyraznego zakończenia tej transakcji po wykonaniu kolejnego polecenia, ale
w trybie OCI_COMMIT_ON_SUCCESS.
W rzeczywistości oczywiście może zajść potrzeba użycia więcej niż tylko dwóch poleceń
w transakcji. W tym celu można wykonać w trybie OCI_DEFAULT wszystkie polecenia (poza ostat-
nim), które mają zostać zgrupowane w pojedynczej transakcji, a następnie wykonać ostatnie
polecenie w trybie OCI_COMMIT_ON_SUCCESS  transakcja będzie zakończona.
Graficzne przedstawienie całego procesu pokazano na rysunku 4.3.
Przedstawiony poniżej skrypt prezentuje, w jaki sposób architektura pokazana na rysunku 4.3
może być zaimplementowana w PHP. Warto zauważyć, że w przeciwieństwie do funkcji login(),
omówionej w podrozdziale  Przykład użycia podprogramu składowanego , który znajduje się
w rozdziale 3., przedstawiona poniżej funkcja login() zatrzymuje wykonywanie i zwraca
115
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
Rysunek 4.3. Graficzne przedstawienie procesu wykonywania omówionego powyżej bloku kodu
wartość false, kiedy wstawienie rekordu do tabeli logons zakończy się niepowodzeniem. Ma
to sens, ponieważ obecnie nowy rekord jest wstawiany do tabeli logons nie tylko w celu zapisania
informacji o logowaniu, ale także sprawdzenia, czy wstawiane dane stosują się do zasad bizneso-
wych. Wymienione zasady oznaczają, że do tabeli logons nie może być wstawiony żaden rekord
zawierający w kolumnie log_time datę, dla której dniem tygodnia jest sobota bądz niedziela.
// Plik: userLoginTrans.php.
function login($usr, $pswd) {
if(!$rsConnection = oci_connect('usr', 'usr', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Nie można nawiązać połączenia z bazą danych: '
. $err['message'], E_USER_ERROR);
};
$query = "SELECT full_name, num_logons FROM accounts
WHERE usr_id = :userid AND pswd = :passwd";
$stmt = oci_parse($rsConnection,$query);
oci_bind_by_name($stmt, ':userid', $usr);
oci_bind_by_name($stmt, ':passwd', $pswd);
if (!oci_execute($stmt)) {
$err = oci_error($stmt);
trigger_error('Wykonanie zapytania zakończyło się niepowodzeniem: '
. $err['message'], E_USER_ERROR);
}
if (!$arr = oci_fetch_array($stmt, OCI_ASSOC)) {
print "Podano nieprawidłową nazwę użytkownika lub hasło.";
return false;
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 zakończyła 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 zakończyła się niepowodzeniem: '
. $err['message'], E_USER_WARNING);
if ($err['code']=='02290'){
print "Nie można nawiązać połączenia w sobotę lub niedzielę.";
}
return false;
}
print "Witaj ".$arr['FULL_NAME']."
";
print "Odwiedziłeś nas już ".$num_logons." raz(y)";
session_start();
$_SESSION['user']=$usr;
return true;
}
?>
Jak już wcześniej wspomniano, kolumna num_logons w tabeli accounts przechowuje liczbę
zakończonych powodzeniem operacji logowania każdego użytkownika. W skrypcie zdefinio-
wano polecenie UPDATE, zwiększające wartość pola num_logons w rekordzie reprezentującym
użytkownika, którego dane uwierzytelniające zostały użyte podczas procesu uwierzytelniania.
Poprzez wykonanie tego polecenia w trybie OCI_DEFAULT następuje utworzenie nowej transak-
cji. Ma to sens, ponieważ wówczas istnieje możliwość wycofania zmian wprowadzonych przez
operację UPDATE, jeśli kolejna operacja wstawiania danych do tabeli logons zakończy się nie-
powodzeniem.
Jeżeli wykonanie operacji UPDATE zakończy się niepowodzeniem, nastąpi opuszczenie funkcji
login() i zwrócenie wartości false wywołującemu ją skryptowi. W ten sposób skrypt otrzy-
muje informacje, że uwierzytelnienie nie powiodło się.
Następnie w omawianym przykładzie zdefiniowane jest polecenie INSERT, które jest wykonywane
po zakończonym powodzeniem uwierzytelnieniu użytkownika. Wykonanie go powoduje zwięk-
szenie licznika informującego o przeprowadzonej liczbie prawidłowych operacji logowania.
117
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
Wykonanie polecenia INSERT w trybie OCI_COMMIT_ON_SUCCESS w omawianym skrypcie gwa-
rantuje, że transakcja zostanie zatwierdzona w przypadku powodzenia operacji, a w przeciw-
nym razie wycofana. Oznacza to, że zmiany wprowadzone zarówno przez polecenie INSERT,
jak i UPDATE albo staną się trwałe, albo będą wycofane.
Jak Czytelnik zapewne pamięta, wartością zwrotną funkcji oci_error() jest tablica asocjacyj-
na dwóch elementów. Pierwszy z nich  code  zawiera kod błędu Oracle, a drugi, message
 komunikat błędu opisujący ten błąd. W omawianym przykładzie następuje sprawdzenie,
czy kod błędu Oracle jest równy 02290. Jeżeli tak, oznacza to wystąpienie błędu związanego
ze złamaniem nałożonego ograniczenia. Ponieważ w tabeli logons zdefiniowano tylko jedno
ograniczenie (uniemożliwiające wstawiania nowych rekordów do tabeli logons w soboty i nie-
dziele), można poinformować użytkownika o braku możliwości uzyskania połączenia w sobotę
bądz niedzielę.
Jeżeli w omawianym skrypcie wykonanie polecenia INSERT zakończy się niepowodzeniem,
nastąpi zakończenie działania funkcji login() wraz z wartością zwrotną false. Wskazuje to
wywołującemu ją skryptowi, że uwierzytelnienie nie powiodło się. W przypadku uwierzytelnienia
zakończonego powodzeniem można podjąć odpowiednie działania, na przykład wyświetlić
komunikat powitania i utworzyć nową sesję.
Aby teraz zobaczyć w działaniu nowo utworzoną funkcję login(), można wykorzystać poniż-
szy prosty skrypt:
// Plik: testLoginTrans.php.
require_once "userLoginTrans.php";
if (login('bob','pswd')) {
if (isset($_SESSION['user'])) {
print '

'.'Twoja nazwa konta: '.$_SESSION['user'].'

';
} else {
print '

'.'Zmienna sesji przedstawiająca nazwę konta
nie została ustawiona.'.'

';
}
}else {
print '

'.'Uwierzytelnienie zakończyło się niepowodzeniem'.'

';
}
?>
Jeżeli przedstawiony powyżej skrypt testLoginTrans.php zostanie uruchomiony w sobotę lub
niedzielę, spowoduje wyświetlenie następującego komunikatu:
Nie można nawiązać połączenia w sobotę lub niedzielę.
Uwierzytelnienie zakończyło się niepowodzeniem.
Jednak uruchomienie skryptu w dniu roboczym spowoduje wygenerowanie następujących
danych wyjściowych:
118
Rozdział 4. " Transakcje
Witaj Bob Robinson
Odwiedziłeś nas już 1 raz(y)
Twoja nazwa konta: bob
Każde kolejne uruchomienie skryptu testLoginTrans.php w dniu roboczym spowoduje zwięk-
szenie licznika odwiedzin strony przez użytkownika Bob Robinson. Jednak wykonanie tego
skryptu w sobotę lub niedzielę nie powoduje zwiększenia wartości licznika. Powyższy test
udowadnia, że wszystko działa zgodnie z założeniami.
Przenoszenie kodu transakcyjnego do bazy danych
Teraz, gdy Czytelnik dysponuje już działającym rozwiązaniem transakcyjnym zaimplemento-
wanym przede wszystkim w PHP, warto zastanowić się, w jaki sposób można zmniejszyć ilość
kodu PHP poprzez przeniesienie części logiki biznesowej aplikacji do bazy danych.
Używanie wyzwalaczy
Pracę można rozpocząć od zdefiniowania wyzwalacza BEFORE INSERT, obejmującego tabelę
logons, który będzie automatycznie uaktualniał tabelę accounts poprzez zwiększanie wartości
pola num_logons w odpowiednim rekordzie. W ten sposób zostanie wyeliminowana potrzeba
wykonywania tej operacji UPDATE z poziomu kodu PHP.
Przedstawione poniżej polecenie SQL powoduje utworzenie wyzwalacza. Należy je wykonać
z poziomu narzędzia SQL*Plus po nawiązaniu połączenia 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;
/
Po utworzeniu przedstawionego powyżej wyzwalacza logons_insert należy z kodu funkcji
login(), która znajduje się w skrypcie userLoginTrans.php, usunąć poniższy fragment:
$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 zakończyła się niepowodzeniem: '
. $err['message'], E_USER_WARNING);
119
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
return false;
}
oci_free_statement($stmt);
Warto zwrócić uwagę, że powyższa modyfikacja funkcji login() nie wymaga zmiany istnieją-
cego kodu, który implementuje tę funkcję. Dlatego też w celu sprawdzenia uaktualnionej wersji
funkcji login() nadal można wykorzystać skrypt testLoginTrans.php, przedstawiony w poprzed-
niej sekcji. Powinien on wygenerować takie same jak poprzednio dane wyjściowe.
Wycofanie na poziomie polecenia
Przeglądając kod uaktualnionej funkcji login(), Czytelnik może zauważyć, że nie powoduje
ona wykonania jakiegokolwiek polecenia w trybie OCI_DEFAULT, a tym samym nie tworzy
transakcji. Zamiast tego polecenie INSERT jest wykonywane w trybie OCI_COMMIT_ON_SUCCESS.
Oznacza to, że każdy błąd wykryty podczas działania polecenia INSERT spowoduje wycofanie
wszystkich zmian wprowadzonych przez polecenie INSERT. Z kolei jeżeli wykonanie polecenia
INSERT zakończy się powodzeniem, zmiany zostaną automatycznie zatwierdzone.
Jak dotąd wszystko idzie dobrze. Co się jednak zdarzy, jeśli polecenie UPDATE, wywołane z po-
ziomu wyzwalacza, zakończy swoje działanie niepowodzeniem? Czy spowoduje to wycofanie
zmian wprowadzonych przez polecenie INSERT? Prostym testem pomagającym w odpowiedzi na
te pytania jest tymczasowe zmodyfikowanie polecenia UPDATE w wyzwalaczu logons_trigger
w taki sposób, aby działanie operacji UPDATE zawsze kończyło się niepowodzeniem. Następnie
należy uruchomić skrypt testLoginTrans.php, który omówiono w podrozdziale  Nadzorowanie
transakcji z poziomu PHP , i zobaczyć co się zdarzy.
W celu ponownego utworzenia wyzwalacza, aby wykonanie jego polecenia UPDATE zawsze
kończyło się niepowodzeniem, można użyć przedstawionego poniżej polecenia SQL:
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;
END;
/
Trzeba koniecznie zwrócić uwagę na fakt, że chociaż wykonanie polecenia UPDATE w wyzwala-
czu zawsze będzie kończyło się niepowodzeniem, sam wyzwalacz zostanie poprawnie skom-
pilowany.
Teraz, po uruchomieniu skryptu testLoginTrans.php, powinny zostać wyświetlone następujące
dane wyjściowe:
Uwierzytelnienie zakończyło się niepowodzeniem
120
Rozdział 4. " Transakcje
Jak można się przekonać, proces uwierzytelniania zakończył się niepowodzeniem. Aby upew-
nić się, że wykonanie polecenia INSERT w tabeli logons również będzie miało taki wynik, można
obliczać liczbę rekordów tabeli przed i po wykonaniu skryptu testLoginTrans.php. Taki efekt
da się osiągnąć za pomocą poniższego polecenia SQL, które wydano z poziomu narzędzia
SQL*Plus po nawiązaniu połączenia z bazą danych jako usr/usr:
SELECT count(*) FROM logons;
Czytelnik powinien przekonać się, że po wykonaniu skryptu testLoginTrans.php liczba rekordów
tabeli logons pozostanie taka sama. Stanowi to dowód, że niepowodzenie uaktualnienia tabeli
logons za pomocą wyzwalacza logons_insert BEFORE INSERT, obejmującego tabelę accounts, po-
woduje także wycofanie zmian wprowadzonych przez polecenie INSERT.
Ogólnie rzecz biorąc, jeżeli w trakcie wykonywania wyzwalacza nastąpi błąd, wycofane zostaną wszyst-
kie operacje, które spowodowały uruchomienie go. Wynika to z działania tak zwanego wycofywana na
poziomie rekordu  czyli każdy błąd powstały podczas wykonywania polecenia spowoduje wycofanie
wszystkich zmian wprowadzonych przez to polecenie.
Jednak powyższe stwierdzenie nie zawsze jest prawdziwe. Na przykład wyzwalacz logons_insert
może zostać zaimplementowany w taki sposób, że efekty działania polecenia INSERT nie zo-
staną wycofane, kiedy wykonanie polecenia UPDATE z tego wyzwalacza zakończy się niepowo-
dzeniem. Warto przeanalizować przedstawioną poniżej 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ć wyświetlone dane wyj-
ściowe o podobnej postaci:
Witaj Bob Robinson
Odwiedziłeś nas już 3 raz(y)
Twoja nazwa konta: bob
Następnie jeżeli skrypt zostanie uruchomiony ponownie, wyświetlana liczba operacji logowa-
nia pozostanie taka sama. Jednak po sprawdzeniu liczby rekordów tabeli logons, jak przed-
stawiono we wcześniejszej części podrozdziału, będzie można dostrzec, że kolejne wykonanie
skryptu testLoginTrans.php spowodowało zwiększenie wartości tej liczby.
121
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
Wskazuje to, że chociaż wykonanie polecenia UPDATE z wyzwalacza zakończyło się niepowo-
dzeniem, wykonanie polecenia INSERT zakończyło się powodzeniem. Wynika to z faktu, że
omówiony powyżej wyzwalacz logons_insert powoduje ciche zignorowanie jakiegokolwiek błędu
zgłaszanego podczas jego wykonywania. W sekcji WHEN OTHERS  która jest jedyną procedurą
obsługi wyjątków w części wyzwalacza odpowiedzialnej za obsługę wyjątków  ustalono
wartość NULL.
W większości przypadków zastosowanie powyżej techniki nie jest zalecane, ponieważ powoduje zmianę
oczekiwanego sposobu zachowania bazy danych. Rozsądne założenie jest takie, że jeśli podczas wyko-
nywania jakiegokolwiek polecenia SQL wystąpi błąd, modyfikacje wprowadzone przez to polecenie są
automatycznie wycofywane.
Dlatego też zamiast ustawiać wartość NULL w procedurze obsługi wyjątków, należy utworzyć
kod, który będzie podejmował odpowiednie działania w odpowiedzi na wystąpienie błędu.
Na przykład można wykorzystać procedurę RAISE_APPLICATION_ERROR w celu wygenerowania
zdefiniowanego przez użytkownika błędu ORA. Znajdujący się poniżej fragment kodu poka-
zuje, w jaki sposób wyzwalacz logons_insert mógłby zostać zmodyfikowany, aby wywoływać
RAISE_APPLICATION_ERROR z poziomu procedury obsługi błędów:
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 powiodło się.');
END;
/
W powyższym wyzwalaczu procedura obsługi wyjątków wyraznie wywołuję procedurę RAISE_
APPLICATION_ERROR w celu wygenerowania błędu ORA zdefiniowanego przez użytkownika.
Jeżeli w pliku konfiguracyjnym php.ini parametr display_errors ma wartość On, omówiony w pod-
rozdziale  Nadzorowanie transakcji z poziomu PHP skrypt userLoginTrans.php spowoduje wyświetlenie
komunikatu błędu wskazanego jako drugi parametr procedury RAISE_APPLICATION_ERROR.
Teraz, po uruchomieniu skryptu testLoginTrans.php, powinien zostać wyświetlony następują-
cy komunikat:
Uwierzytelnienie zakończyło się niepowodzeniem
122
Rozdział 4. " Transakcje
Liczba rekordów 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 również przez polecenie INSERT.
Przed zakończeniem pracy z tym przykładem należy się upewnić o ponownym utworzeniu
wyzwalacza logons_trigger, aby klauzula SET w jego poleceniu UPDATE przedstawiała się na-
stępująco:
SET num_logons = num_logons + 1
Rozważania dotyczące izolacji transakcji
Kiedy transakcja modyfikuje rekord tabeli bazy danych, Oracle nakłada na niego blokadę
utrzymywaną aż do chwili zatwierdzenia bądz wycofania tej transakcji. Celem takiego zacho-
wania jest niedopuszczenie do sytuacji, w której dwie współbieżne transakcje będą modyfi-
kowały ten sam rekord.
Bardzo ważne jest, aby w tym miejscu wspomnieć, że rekordy z nałożonymi blokadami nadal
mogą być odczytywane zarówno przez transakcje uaktualniające rekordy, jak i inne transak-
cje. Różnica między dwiema wymienionymi transakcjami polega na tym, że transakcja nakła-
dająca blokadę może zauważyć zmiany natychmiast po wykonaniu polecenia modyfikującego
te rekordy. Natomiast inna transakcja nie może zobaczyć tych zmian aż do chwili zatwierdze-
nia zmian przez transakcję, która nałożyła blokadę.
Podczas gdy zastosowany w bazie danych Oracle mechanizm nakładania blokad został szcze-
gółowo omówiony w dokumentacji Oracle (w rozdziale  Data Concurrency and Consistency
w podręczniku użytkownika Oracle Database Concepts), w podrozdziale przedstawiono ogól-
ny opis działania izolacji transakcji w aplikacjach PHP/Oracle.
Którą funkcję rozszerzenia OCI8 służącą do nawiązywania
połączenia należy wybrać?
Jak Czytelnik pamięta z podrozdziału  Funkcje OCI8, które służą do nawiązywania połączenia
z Oracle znajdującego się w rozdziale 2., rozszerzenie PHP OCI8 oferuje trzy różne funkcje słu-
żące do nawiązywania połączenia z bazą danych Oracle. Są to oci_connect(), oci_new_connect()
oraz oci_pconnect(). Jedyną różnicą między tymi funkcjami jest rodzaj nawiązywanego połą-
czenia z bazą danych.
Zarówno funkcja oci_connect(), jak i oci_pconnect() używają bufora połączenia bazy danych,
tym samym eliminując koszt nawiązania połączenia w trakcie każdego żądania. Różnica między
dwiema wymienionymi funkcjami polega na tym, że połączenie nawiązane przez oci_connect()
123
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
zostaje zwolnione po zakończeniu wykonywania skryptu, podczas gdy połączenia nawiązane
przez funkcję oci_pconnect() są trwałe między wykonywaniem kolejnych skryptów.
W przeciwieństwie do oci_connect() i oci_pconnect() funkcja oci_new_connect() nie używa
bufora połączeń i zawsze powoduje nawiązanie nowego połączenia. Trzeba ją stosować, gdy
zachodzi potrzeba utworzenia w skrypcie dwóch lub większej liczby współbieżnych transak-
cji. Przedstawiony poniżej przykład prezentuje użycie funkcji oci_new_connect() w działaniu.
W poniższym skrypcie warto zwrócić uwagę na wykorzystanie funkcji oci_new_connect() w celu
utworzenia współbieżnej transakcji. Chociaż do utworzenia pierwszej transakcji w skrypcie
użyto funkcji oci_connect(), w celu nawiązania nowego połączenia zastosowano funkcję oci_
new_connect().
// Plik: newConns.php.
function select_emp_job ($conn, $jobno) {
$query = "SELECT employee_id, first_name, last_name, salary
FROM employees WHERE job_id =:jobid";
$stmt = oci_parse($conn,$query);
oci_bind_by_name($stmt, ':jobid', $jobno);
if (!oci_execute($stmt, OCI_DEFAULT)) {
$err = oci_error($stmt);
trigger_error('Wykonanie zapytania zakończyło się niepowodzeniem: '
. $err['message'], E_USER_ERROR);
};
print '';
while ($emp = oci_fetch_array($stmt, OCI_ASSOC)) {
print '';
print '';
print '';
print '';
print '';
}
print '
'.$emp['EMPLOYEE_ID'].''.$emp['FIRST_NAME'].' '.$emp['LAST_NAME']. ''.$emp['SALARY'].'
';
}
if(!$conn1 = oci_connect('hr', 'hr', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Nie można nawiązać połączenia z bazą danych: '
. $err['message'], E_USER_ERROR);
};
if(!$conn2 = oci_new_connect('hr', 'hr', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Nie można nawiązać połączenia z bazą danych: '
. $err['message'], E_USER_ERROR);
};
$jobno = 'AD_VP';
$query = "UPDATE employees SET salary = 18000 WHERE job_id=:jobid";
$stmt = oci_parse($conn1,$query);
oci_bind_by_name($stmt, ':jobid', $jobno);
124
Rozdział 4. " Transakcje
if (!oci_execute($stmt, OCI_DEFAULT)) {
$err = oci_error($stmt);
trigger_error('Wykonanie zapytania zakończyło się niepowodzeniem: '
. $err['message'], E_USER_ERROR);
};
print "

Testowanie izolacji transakcji!

";
print "

Transakcja A w ramach połączenia conn1:

";
print "

(wyniki po uaktualnieniu oraz przed zatwierdzeniem transakcji
w ramach połączenia conn1)

";
select_emp_job($conn1, $jobno);
print "

Transakcja B w ramach połączenia conn2:

";
print "

(wyniki po uaktualnieniu oraz przed zatwierdzeniem transakcji
w ramach połączenia conn1)

";
select_emp_job($conn2, $jobno);
if (!oci_commit($conn1)) {
$err = oci_error($conn1);
trigger_error('Zatwierdzenie transakcji zakończyło się niepowodzeniem: '
.$err['message'], E_USER_ERROR);
}
print "

Transakcja B w ramach połączenia conn2:

";
print "

( wyniki po uaktualnieniu oraz po zatwierdzeniu transakcji
w ramach połączenia conn1)

";
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 zakończyło się niepowodzeniem: '
. $err['message'], E_USER_ERROR);
};
?>
Na rysunku 4.4 pokazano dane wyjściowe powyższego skryptu. Jak można zobaczyć, zmiany
wprowadzone przez operację UPDATE przeprowadzoną w transakcji działającej w ramach połą-
czenia conn1 z bazą danych są widoczne wewnątrz tej transakcji natychmiast po wykonaniu
polecenia UPDATE. Nie są natomiast widoczne dla współbieżnej transakcji działającej w ramach
połączenia conn2 aż do chwili zatwierdzenia pierwszej transakcji.
Powracając do kodu zródłowego omawianego skryptu newConns.php: warto zwrócić uwagę,
że wszystkie polecenia SQL użyte w skrypcie zostały wykonane w trybie OCI_DEFAULT. Gwa-
rantuje on natychmiastowe zatwierdzenie transakcji.
Należy również zwrócić uwagę, że pierwsze połączenie w skrypcie zostało nawiązane za po-
mocą funkcji oci_connect(). Ponieważ jest to pierwsze połączenie, bufor połączeń przypisany
temu skryptowi jest pusty, więc funkcja oci_connect() nawiąże nowe połączenie z bazą danych.
125
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
Rysunek 4.4. Testowanie izolacji transakcji
Transakcje współbieżne trzeba tworzyć z użyciem transakcyjnie izolowanych połączeń. Podczas gdy
pierwsze połączenie w skrypcie może być utworzone za pomocą funkcji oci_connect(), w celu utworzenia
kolejnych, transakcyjnie izolowanych połączeń w skrypcie trzeba wykorzystać funkcję oci_new_connect().
Następnie w celu utworzenia nowego, transakcyjnie izolowanego połączenia w skrypcie na-
stępuje wywołanie funkcji oci_new_connect().
Poprzez wykonanie polecenia UPDATE w trybie OCI_DEFAULT w ramach połączenia conn1 nastę-
puje utworzenie transakcji w tym połączeniu.
Po wykonaniu polecenia UPDATE efekty tej operacji będą widoczne dla innych operacji prze-
prowadzanych wewnątrz tej samej transakcji. Aby to udowodnić, w skrypcie wyświetlane są
rekordy zmodyfikowane przez polecenie UPDATE wewnątrz tej samej transakcji przed jej za-
twierdzeniem. Jak widać na rysunku 4.4, polecenie SELECT zwraca nowe wartości uaktualnio-
nych rekordów.
126
Rozdział 4. " Transakcje
Jednak podczas przeprowadzania operacji SELECT we współbieżnej transakcji nadal będą wi-
doczne początkowe wartości uaktualnionych rekordów. Wynika to z faktu, że współbieżne trans-
akcje są izolowane od zmian wprowadzonych przez niezatwierdzone transakcje. Gdy transak-
cja zostanie zatwierdzona, wszystkie wprowadzone przez nią zmiany staną się widoczne dla
innych transakcji.
Wreszcie, za pomocą funkcji UPDATE następuje przywrócenie wartości początkowych uaktual-
nionym rekordom.
Kwestie związane z współbieżnym uaktualnianiem
Podczas projektowania aplikacji, która będzie modyfikowała dane bazy danych w środowisku
wielodostępnym, programista będzie musiał zmierzyć się z dwoma wyzwaniami:
Zapewnienie poprawności i spójności danych.
Zagwarantowanie, że wydajność nie ucierpi z powodu nakładania blokad.
Chociaż Oracle dostarcza zestaw funkcji, które mogą pomóc osiągnąć wymienione powyżej
cele, prawidłowe wykorzystanie ich jest już zadaniem programisty. Przedstawione poniżej
podrozdziały koncentrują się na pewnych kwestiach związanych z współbieżnym uaktualnia-
niem danych, które mogą pojawić się w środowisku wielodostępnym podczas nieprawidłowe-
go używania transakcji.
Kwestie związane z nakładaniem blokad
Jak już wcześniej wspomniano, Oracle nakłada blokadę na uaktualniany rekord, aby inne
transakcje nie miały możliwości jego modyfikacji aż do zakończenia uaktualniającej go trans-
akcji. Podczas gdy celem takiego zachowania jest zagwarantowanie poprawności danych uży-
wanych w środowisku wielodostępnym, w kiepsko zaprojektowanej aplikacji może ono spo-
wodować powstanie znaczącego obciążenia.
Warto przyjrzeć się prostemu skryptowi pokazującemu, jak kiepsko zaprojektowany skrypt,
który wykonuje długotrwające operacje, może spowodować problemy związane z nakładaniem
blokad, jeśli zostanie użyty w środowisku wielodostępnym. Przedstawiony poniżej skrypt
updateSleep.php wykonuje następujące kroki:
Tworzy transakcję.
Uaktualnia niektóre rekordy w tabeli employees.
Wstrzymuje na 20 sekund wykonywanie skryptu.
Wycofuje transakcję.
Wstrzymanie wykonywania poniższego skryptu za pomocą funkcji sleep() pozwala na symu-
lację przeprowadzania operacji wymagającej ogromnej ilości obliczeń.
127
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
// Plik: updateSleep.php.
if(!$dbConn = oci_connect('hr', 'hr', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Nie można nawiązać połączenia 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 zakończyło się niepowodzeniem: '
. $err['message'], E_USER_ERROR);
};
$updrows = oci_num_rows($stmt);
print 'Uaktualniono '.$updrows. ' rekord(ów)'.'
';
sleep(20);
oci_rollback($dbConn);
print 'Transakcja została wycofana.';
?>
W powyższym skrypcie polecenie UPDATE jest wykonywane w trybie OCI_DEFAULT, nakazując
bazie danych Oracle utworzenie transakcji.
Następnie wywołanie funkcji sleep() powoduje wstrzymanie wykonywania skryptu na dwa-
dzieścia sekund, symulując tym samym przeprowadzanie operacji wymagającej ogromnej ilo-
ści obliczeń.
Wreszcie, za pomocą funkcji oci_rollback() następuje wycofanie transakcji. W rzeczywisto-
ści ten krok jest opcjonalny, ponieważ transakcja zostaje wycofana automatycznie, gdy skrypt
kończy działanie.
Teraz uruchomiony skrypt updateSleep.php, który omówiono wcześniej, uaktualni wszystkie
rekordy Administration Vice President w tabeli employees, zablokuje je na dwadzieścia se-
kund, a następnie wycofa transakcję.
Jeżeli w trakcie wymienionych dwudziestu sekund wystąpi próba uaktualnienia tych samych
rekordów, ale z poziomu innego skryptu, na przykład za pomocą narzędzia SQL*Plus, będą
one zablokowane, aż skrypt updateSleep.php zwolni blokadę nałożoną na wymienione rekordy.
Morał z powyższego skryptu jest taki, że jeśli skrypt ma przeprowadzić długotrwającą opera-
cję w bardzo obciążonym środowisku wielodostępnym, zawsze dobrym pomysłem będzie za-
mknięcie wszystkich aktywnych transakcji wewnątrz skryptu przed rozpoczęciem przetwa-
rzania tej operacji.
128
Rozdział 4. " Transakcje
Utracone uaktualnienia
Omówiony powyżej przykład pokazał, jak kiepsko zaprojektowana aplikacja transakcyjna mo-
że na długo zablokować zasoby bazy danych, co uniemożliwi współbieżnym transakcjom uzy-
skanie dostępu do tych zasobów w rozsądnym czasie. Jednak warto zwrócić uwagę, że zasto-
sowanie podejścia bez blokad podczas modyfikowania danych bazy danych w środowisku
wielodostępnym może spowodować inny problem  utracone uaktualnienia. Aby zrozumieć,
na czym polega problem utraconych uaktualnień, należy przeanalizować kroki, które interak-
tywna aplikacja zwykle przeprowadza podczas modyfikacji informacji przechowywanych
w bazie danych:
Wybór danych z bazy danych.
Wyświetlenie danych użytkownikowi.
Oczekiwanie na działanie użytkownika.
Uaktualnienie danych w bazie danych.
Powinno być całkiem oczywiste, że kiedy w powyższym przykładzie aplikacja czeka na dzia-
łanie użytkownika, inny użytkownik może spowodować zmianę danych. Dlatego też jeżeli
pierwszy użytkownik będzie kontynuował proces uaktualniania danych, zmiany wprowadzone
przez drugiego użytkownika zostaną utracone.
Problem można lepiej zrozumieć na podstawie przykładu. Warto więc spojrzeć na skrypt
updateQuickForm.php, który implementuje powyższe kroki za pomocą pakietu PEAR o nazwie
HTML_QuickForm. Po uruchomieniu skryptu wykona ona następujące kroki:
Uaktualni dwa rekordy w tabeli employees.
Wygeneruje formularz proszący użytkownika o zatwierdzenie bądz wycofanie zmian.
Zakończy działanie, wycofując wprowadzone zmiany.
Za pomocą formularza wygenerowanego przez skrypt użytkownik może albo zatwierdzić, albo
wycofać zmiany, a następnie musi kliknąć przycisk Wyślij. Gdy to zrobi, skrypt zostanie wy-
wołany ponownie, tym razem wykonując poniższe kroki:
Uaktualnienie tych samych rekordów w tabeli employees.
Zatwierdzenie lub wycofanie zmian w zależności od decyzji użytkownika.
Zakończenie działania skryptu.
Dane wyjściowe wygenerowane przez skrypt zostały pokazane na rysunku 4.5.
Jednak przed uruchomieniem skryptu updateQuickForm.php należy się upewnić o zainstalo-
waniu pakietu PEAR::HTML_QuickForm. Ponieważ pakiet HTML_QuickForm jest uzależniony od
innego pakietu o nazwie HTML_Common, w pierwszej kolejności należy zainstalować drugi z wymie-
nionych. Zakładając, że w systemie znajduje się zainstalowany i skonfigurowany PEAR Installer,
w celu pobrania i instalacji pakietu HTML_Common można wydać poniższe polecenie:
$ pear install HTML_Common
129
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
Rysunek 4.5. Dane wyjściowe skryptu updateQuickForm.php wyświetlone w przeglądarce internetowej
Po instalacji pakietu HTML_Common można przystąpić do pobrania i instalacji pakietu HTML_Quick
Form. Należy wydać polecenie:
$ pear install HTML_QuickForm
Po przeprowadzeniu powyższych kroków można uruchomić skrypt updateQuickForm.php, które-
go kod zródłowy przedstawiono poniżej:
// Plik: updateQuickForm.php.
require_once 'HTML/QuickForm.php';
if(!$dbConn = oci_connect('hr', 'hr', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Nie można nawiązać połączenia 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 zakończyło się niepowodzeniem: '
.$err['message'], E_USER_ERROR);
};
print '

Potwierdzenie uaktualnienia!

';
$updrows = oci_num_rows($stmt);
130
Rozdział 4. " Transakcje
$frm=new HTML_QuickForm('frm1', 'POST');
$frm->addElement('header','msg1','Uaktualniono '.$updrows. '
rekord(ów). Czy chcesz zatwierdzić zmiany?');
$grp[] =& HTML_QuickForm::createElement('radio', null, null,'zatwierdz', 'C');
$grp[] =& HTML_QuickForm::createElement('radio', null, null,'wycofaj', 'R');
$frm->addGroup($grp, 'trans');
$frm->setDefaults(array('trans' => 'C'));
$frm->addElement('submit','submit','Wyślij');
if(isset($_POST['submit'])) {
if ($_POST['trans']=='C'){
oci_commit($dbConn);
print 'Transakcja została zatwierdzona.';
} elseif ($_POST['trans']=='R'){
oci_rollback($dbConn);
print 'Transakcja została wycofana.';
} else {
$frm->display();
}
} else {
$frm->display();
}
?>
Przyglądając się formularzowi pokazanemu na rysunku 4.5, Czytelnik może pomyśleć, że po
uaktualnieniu dwóch rekordów tabeli employees skrypt czeka na działanie użytkownika, pozo-
stawiając aktywną transakcję. W rzeczywistości działa on zupełnie inaczej.
Skrypt podczas pierwszego uruchamiania faktycznie powoduje uaktualnienie tabeli employees, ale
następnie wycofuje transakcję. W ten sposób możliwe jest zliczenie liczby rekordów modyfi-
kowanych przez polecenie UPDATE i podanie tej informacji użytkownikowi. Gdy ten wybierze
opcję zatwierdz albo wycofaj transakcji i kliknie przycisk Wyślij, skrypt ponownie wykona tę
samą operację UPDATE. Tym razem w zależności od opcji wybranej przez użytkownika zmiany
wprowadzone przez polecenie UPDATE będą zatwierdzone lub wycofane.
Zaletą stosowania powyższej techniki jest to, że przetwarzane rekordy nie zostają zablokowa-
ne w czasie potrzebnym na podjęcie decyzji, który przycisk kliknąć: zatwierdz lub wycofaj.
Dzięki temu w tym samym czasie inne transakcje mogą przeprowadzać operacje na tych re-
kordach. Jednak powoduje to powstanie kolejnego problemu  utraconych uaktualnień, jak
to zostało omówione we wcześniejszej części podrozdziału.
Aby uniknąć problemu utraconych uaktualnień, można zastosować strategię blokowania optymi-
stycznego, dzięki czemu wartości uaktualnianych pól nie zostaną zmienione, kiedy użytkow-
nik rozpocznie z nimi pracę.
131
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
Transakcje autonomiczne
Kontynuując poprzedni przykład: może wystąpić potrzeba rejestrowania prób uaktualnienia
rekordów w tabeli employees. Aby umożliwić takie zadanie, trzeba utworzyć tabelę przechowującą
sprawdzane rekordy, jak również wyzwalacz BEFORE UPDATE, który obejmuje tabelę employees.
Zadaniem wyzwalacza będzie wstawianie rekordu do nowej tabeli za każdym razem, gdy kto-
kolwiek spróbuje uaktualnić rekord w tabeli employees.
W celu utworzenia wymienionej struktury danych należy wydać następujące 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 wystąpił błąd.');
END;
/
Po wykonaniu powyższego polecenia można uruchomić skrypt updateQuickForm.php, omó-
wiony w poprzednim podrozdziale, i sprawdzić, czy mechanizm kontroli działa zgodnie z za-
łożeniami. W formularzu wygenerowanym przez skrypt należy zaznaczyć opcję wycofaj, a na-
stępnie kliknąć przycisk Wyślij. Teraz przy próbie wyświetlenia rekordów tabeli emp_updates
w następujący sposób:
CONN usr/usr;
SELECT * FROM emp_updates;
Czytelnik powinien zobaczyć, że tabela emp_updates wciąż nie zawiera żadnych rekordów:
no rows selected
Oznacza to, że po wycofaniu operacji UPDATE nastąpiło wycofanie rekordu także z tabeli kon-
trolnej. Takie zachowanie jest oczekiwane, ponieważ nie można wycofać pewnych efektów
transakcji  można albo zatwierdzić wszystkie efekty, albo wszystkie wycofać.
132
Rozdział 4. " Transakcje
Próba zatwierdzenia jedynie polecenia INSERT wykonywanego wewnątrz wyzwalacza zakoń-
czy się niepowodzeniem, ponieważ w wyzwalaczu niedozwolone jest użycie żadnych poleceń
nadzorujących transakcję. Dlatego też po próbie utworzenia wyzwalacza emp_updates_trigger
w następujący sposób (trzeba pamiętać, aby być połączonym jako użytkownik hr  wykonu-
jąc polecenie CONN hr/hr  inaczej tabela employees nie będzie widoczna i nie uda się utwo-
rzyć 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;
/
uruchomienie skryptu updateQuickForm.php spowoduje wyświetlenie następujących komu-
nikatów błędów:
Warning: oci_execute()[function.oci-execute]:ORA-04092: cannot
ROLLBACK in a trigger ORA-06512: at "HR.EMP_UPDATES_TRIGGER", line
6 ORA-04092: cannot COMMIT in a trigger ORA-04088: error during
execution of trigger 'HR.EMP_UPDATES_TRIGGER'
Fatal error: Query failed: ORA-04092: cannot ROLLBACK in a trigger
ORA-06512: at "HR.EMP_UPDATES_TRIGGER", line 6 ORA-04092: cannot
COMMIT in a trigger ORA-04088: error during execution of trigger 'HR.
EMP_UPDATES_TRIGGER
Powyższe komunikaty błędów zostaną wyświetlone tylko wtedy, gdy w pliku konfiguracyjnym php.ini
parametr display_errors ma ustawioną wartość On.
Jednym ze sposobów rozwiązania powyższego problemu jest użycie transakcji autonomicznej.
Transakcja autonomiczna jest transakcją wewnątrz innej transakcji. Ponieważ jest całkowicie niezależna
od wywołującej ją transakcji, pozwala na przeprowadzanie operacji SQL, a następnie zatwierdzanie ich
lub wycofywanie bez zatwierdzania lub wycofywania wywołującej ją transakcji.
Zastosowanie transakcji autonomicznej w omawianym przykładzie pozwoli na zatwierdzenie
polecenia INSERT wykonywanego przez wyzwalacz emp_updates_trigger niezależnie od transakcji
133
PHP i Oracle. Tworzenie aplikacji webowych: od przetwarzania danych po Ajaksa
utworzonej przez skrypt updateQuickForm.php. Dzięki temu w tabeli emp_updates zostanie
utworzony rekord, nawet gdy efekty operacji UPDATE wywołanej przez ten wyzwalacz zostaną
wycofane.
Przedstawiony poniżej fragment kodu pokazuje sposób utworzenia wyzwalacza emp_updates_
trigger, który stosuje transakcję autonomiczną.
CREATE OR REPLACE TRIGGER emp_updates_trigger
BEFORE UPDATE
ON employees
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO usr.emp_updates VALUES (:new.employee_id,
:new.job_id, SYSDATE);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
/
Powyższy przykład prezentuje implementację transakcji autonomicznej w wyzwalaczu bazy
danych. Zastosowanie tutaj transakcji autonomicznej gwarantuje, że rekord kontrolny zosta-
nie utworzony w tabeli emp_updates, niezależnie od tego, czy operacja UPDATE względem tabeli
employees zostanie zatwierdzona, czy wycofana.
Aby sprawdzić nowo utworzony wyzwalacz, należy ponownie uruchomić skrypt updateQuick-
Form.php i wysłać formularz wygenerowany przez skrypt po wybraniu opcji wycofaj. Następ-
nie trzeba znów wyświetlić zawartość tabeli emp_updates (ponownie trzeba pamiętać o połą-
czeniu na odpowiedniego użytkownika  tym razem usr/usr):
SELECT * FROM emp_updates;
Tym razem dane wyjściowe polecenia mogą być podobne do przedstawionych poniżej:
EMP_ID JOB_ID TIMEDATE
------- ---------- ---------
101 AD_VP 29-MAY-06
102 AD_VP 29-MAY-06
101 AD_VP 29-MAY-06
102 AD_VP 29-MAY-06
Warto zwrócić uwagę, że chociaż próba dotyczyła uaktualnienia tylko dwóch rekordów tabeli
employees, do tabeli kontrolnej emp_updates zostały wstawione cztery rekordy. Trzeba pamię-
tać, że w rzeczywistości skrypt updateQuickForm.php dwukrotnie przeprowadza operację
UPDATE. Po raz pierwszy w celu obliczenia liczby rekordów przeznaczonych do uaktualnienia.
Natomiast druga operacja faktycznie uaktualnia te rekordy.
134
Rozdział 4. " Transakcje
Podsumowanie
Niektóre operacje wykonywane względem bazy danych mają sens jedynie po zgrupowaniu
ich. Klasycznym przykładem jest operacja przelewu środków pieniężnych między dwoma
kontami bankowymi. Jedynym sposobem bezpiecznego przeprowadzenia tego rodzaju opera-
cji pozostaje użycie transakcji. Zastosowanie transakcji pozwala na zgrupowanie poleceń SQL
w logiczne, niewidoczne jednostki pracy, z których każda może być albo w całości zatwier-
dzona, albo w całości wycofana.
W rozdziale Czytelnik dowiedział się, kiedy i jak wykorzystywać transakcje w aplikacjach
PHP/Oracle. Analiza rozpoczęła się od ogólnego przedstawienia transakcji Oracle oraz wyja-
śnienia powodów, dla których programista miałby ich używać w aplikacjach PHP zbudowa-
nych na Oracle. Następnie omówiono organizację aplikacji PHP/Oracle w celu efektywnego
kontrolowania transakcji  skoncentrowano się na korzyściach wynikających z przeniesienia
logiki biznesowej aplikacji transakcyjnej z PHP do bazy danych. Czytelnik dowiedział się
również, które funkcje rozszerzenia OCI8 służące do nawiązywania połączenia należy wybierać
podczas używania transakcji, a także jak tworzyć współbieżne transakcje w ramach tego sa-
mego skryptu. Wreszcie  zaprezentowano wywoływanie niezależnej transakcji z wewnątrz
innej transakcji oraz przedstawiono sytuacje, w których zastosowanie takiego rozwiązania
może być pożądane.
135


Wyszukiwarka

Podobne podstrony:
PHP i MySQL Tworzenie aplikacji WWW phmsap
informatyka php web 2 0 tworzenie aplikacji typu mashup shu wai chow ebook
PHP Web 2 0 Tworzenie aplikacji typu mashup phpw2m
Oracle Database Tworzenie aplikacji internetowych w AJAX i PHP(1)
Oracle?tabaseg Express?ition Tworzenie aplikacji internetowych w PHP or10tw
tworzenie aplikacji w jezyku java na platforme android
Tworzenie aplikacji okienkowych (programowanie)
Facebook tworzenie aplikacji zaproszenie
tworzenie aplikacji na?cebook
PHP i MySQL Witryna WWW oparta na bazie danych Wydanie IV

więcej podobnych podstron