Bazy danych wykład dwunasty Interfejs do połaczeń z baza danych Konrad Zdanowski Uniwersytet Kardynała Stefana Wyszyńskiego, Warszawa danych 1 / 29
Zajmiemy się mechanizmami pozwalajacymi połaczyć się z BD z poziomu aplikacji klienta. Szczegółowo omówimy interfejs JDBC i bazę Oracle, inne mechanizmy sa dość podobne. danych 2 / 29
Nawiazywanie połaczenia Dodajemy do projektu bibliotekę JDBC (Project Properties/Libraries and Classpath/Add Library/Oracle JDBC ) Musimy zaimportować pakiet java.sql ( import java.sql.*). Rejestrujemy sterownik: DriverManager.registerDriver(new oracle.jdbc.driver.oracledriver()); danych 3 / 29
Nawiazywanie połaczenia c.d. Otwieramy połaczenie Connection conn = DriverManager.getConnection(url bazy, użytkownik, hasło). format adresu url: protokół połaczenia @ adres bazy danych:port: sid. parametry znaleźć można w pliku tnsnames.ora (w szczególności SERVICE_NAME = <sid>), np. Connection conn = DriverManager.getConnection( "jdbc:oracle:thin:@localhost:1521:xe", "user", "passwd"); po pracy połaczenie zamykamy conn.close(); danych 4 / 29
Wykonywanie zapytań Otwieramy obiekt implentujacy interfejs zapytań typu Statement. Wykonujemy zapytynie zachowujac wynik w zmiennej typu ResultSet. Typ ten implementuje iterator, którym możemy odczytywać wyniki zapytania. danych 5 / 29
Wykonywanie zapytań Statement stmt = conn.createstatement(); ResultSet rset = stmt.executequery("select TABLE_NAME from USER_TABLES"); while (rset.next()) System.out.println(rset.getString(1)); danych 6 / 29
Wykonywanie zapytań W metodzie ResultSet.getstring() możemy podać: numer atrybutu wyniku zapytania (pierwszy atrybut ma numer 1), jego nazwe (jeśli używamy aliasów select A as B from... podajemy alias). danych 7 / 29
Wykonywanie zapytań przykład import java. s q l. ; class JDBC_Example { public s t a t i c void main ( S t r i n g [ ] args ) { DriverManager. r e g i s t e r D r i v e r ( new o r a c l e. jdbc. d r i v e r. OracleDriver ( ) ) ; Connection conn = DriverManager. getconnection ( " jdbc : oracle : t h i n : @localhost :1521:XE", " c h r i s ", " c h r i s h a s l o " ) ; Statement stmt = conn. createstatement ( ) ; ResultSet r s e t = stmt. executequery ( " s e l e c t TABLE_NAME from USER_TABLES" ) ; while ( r s e t. next ( ) ) System. out. p r i n t l n ( r s e t. g e t S t r i n g ( 1 ) ) ; r s e t. close ( ) ; stmt. close ( ) ; conn. close ( ) ; Konrad } Zdanowski ( Uniwersytet Kardynała Stefana Bazy danych Wyszyńskiego, wykładwarszawa) danych 8 / 29
Odczytywanie wyników zapytania Klasa ResultSet posiada rodzinę funkcji getx (String, Short, Int, Float, Double, TimeStamp,...). Jeżeli wynikiem zapytania jest wartość atrybutu NULL, to w przypadku String, TimeStamp, Date zostanie zwrócony pusty wskaźnik. W przypadku funkcji zwracajacych wartości numeryczne, atrybut o wartości NULL zostanie zwrócony jako zero. Aby rozstrzygnać czy wczytana wartość nie pochodzi od NULL, można użyć metody wasnull, zwracajacej true, jeśli wartość była równa NULL. danych 9 / 29
Odczytywanie wyników zapytania przykład ResultSet r s _ i =stmt. executequery ( " s e l e c t count ( ) as i l o s c from osoby " ) ; r s _ i. next ( ) ; i n t i = r s _ i. g e t I n t ( " i l o s c " ) ; i n t k = r s _ i. g e t I n t ( 1 ) ; danych 10 / 29
Aktualizacja danych Aktualizację danych wykonujemy analogicznie jak zadanie zapytania. Służy temu metoda metoda Statement.executeUpdate(polecenie DML). Możemy wykonać w ten sposób polecenia INSERT, UPDATE, DELETE. Metoda zwraca ilość zmodyfikowanych krotek. Możemy wykonać też polecenie DDL. danych 11 / 29
Transakcje Każde polecenie stanowi jedna transakcję. Jeśli chcemy wyłaczyć/wł aczyć automatyczne zatwierdzanie transakcji możemy użyć metody Connection.setAutoCommit(false/true). Zatwierdzamy wtedy wykonane polecenia metoda Connection.commit(), wycofujemy metoda Connection.rollback(). Polecenie rollback() wycofuje zmiany wprowadzone od czasu ostatniego commit(); Przy zamknięciu połaczenia następuje domyślny commit(). danych 12 / 29
Transakcje przykład conn. setautocommit ( false ) ; Statement stmt = conn. createstatement ( ) ; stmt. executeupdate ( " i n s e r t i n t o osoby values ( Olga, Kwiatkowska ) " ) ; stmt. executeupdate ( " i n s e r t i n t o osoby values ( Iwona, Kwiatkowska ) " ) rs = stmt. executequery ( " s e l e c t imie, nazwisko from osoby " ) ; while ( rs. next ( ) ) System. out. p r i n t l n ( rs. g e t S t r i n g (1)+ " " + rs. g e t S t r i n g ( 2 rs. close ( ) ; conn. r o l l b a c k ( ) ; rs = stmt. executequery ( " s e l e c t imie, nazwisko from osoby " ) ; while ( rs. next ( ) ) System. out. p r i n t l n ( rs. g e t S t r i n g (1)+ " " + rs. g e t S t r i n danych 13 / 29
Transakcje przykład conn. setautocommit ( false ) ; Statement stmt = conn. createstatement ( ) ; stmt. executeupdate ( " i n s e r t i n t o osoby values ( Olga, Kwiatkowska ) " ) ; conn. commit ( ) / / Uwaga zatwierdzamy zmiany! stmt. executeupdate ( " i n s e r t i n t o osoby values ( Iwona, Kwiatkowska ) " ) rs = stmt. executequery ( " s e l e c t imie, nazwisko from osoby " ) ; while ( rs. next ( ) ) System. out. p r i n t l n ( rs. g e t S t r i n g (1)+ " " + rs. g e t S t r i n g ( 2 rs. close ( ) ; conn. r o l l b a c k ( ) ; rs = stmt. executequery ( " s e l e c t imie, nazwisko from osoby " ) ; while ( rs. next ( ) ) System. out. p r i n t l n ( rs. g e t S t r i n g (1)+ " " + rs. g e t S t r i n danych 14 / 29
Zapytania prekompilowane Bezpośrednie przysyłanie zapytania do SZBD jest kosztochłonne. System musi za każdym razem zoptymalizować i skompilować zapytanie. Jeżeli wiemy, że będziemy wykonywac zapytania pewnego typu możemy ograniczyć te koszty, do jednokrotnego wykonania. W samym zapytaniu możemy później zmieniać parametry, z którymi jest wykonywane. danych 15 / 29
Zapytania prekompilowane przykład PreparedStatement ps = conn. preparestatement ( " s e l e c t nazwisko from osoby where imie =? " ) ; ps. s e t S t r i n g ( 1, " Jan " ) ; ResultSet rs=ps. executequery ( ) ; while ( rs. next ( ) ) System. out. p r i n t l n ( rs. g e t S t r i n g ( " nazwisko " ) ) ; rs. close ( ) ; ps. s e t S t r i n g ( 1, "Ewa" ) ; ResultSet rs=ps. executequery ( ) ;... rs. close ( ) ; ps. close ( ) ; danych 16 / 29
Zapytania prekompilowane przykład PreparedStatement s t m t =conn. preparestatement ( " update osoby set hobby=? where rok_urodzenia=? " ) ; stmt. s e t S t r i n g ( 1, " brak " ) ; stmt. s e t I n t ( 2, 2 0 1 2 ) ; stmt. executeupdate ( ) ; danych 17 / 29
Przewijanie wyników Po wynikach zapytania możemy poruszać się w sposób bardziej elastyczny. Musimy utworzyć inaczej obiekt typu Statement: Statement stmt = conn. createstatement ( rodzaj przewijania, r o d z a j odczytu ) ; Rodzaj przewijania przyjać wartość: ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.Type_SCROLL_INSENSITIVE, ResultSet.TYPE_FORWARD_ONLY. Rodzaj odczytu: ResultSet.CONCUR_READ_ONLY, ResultSet.CONCUR_UPDATABLE. danych 18 / 29
Przewijanie wyników Jeśli utworzyliśmy obiekt Statement w powyższy sposób możemy posłuzyć się szeregiem metod clasy ResultSet pozycjonujacych kursor. absolute(n), dla n dodatnich lub ujemnych, relative(n), dla n dodatnich lub ujemnych, first, last next, previous. Możemy sprawdzać aktualna pozycję przez metody isafterlast, isbeforefirst, islast, isfirst. danych 19 / 29
Modyfikowanie relacji wynikowych Jeśli utworzyliśmy zapytanie z parametrem ResultSet.CONCUR_UPDATABLE, możemy modyfikować krotki wyniku. Pola aktualnie odczytywanej krotki modyfikujemy przez metodę ResultSet.UpdateX(nazwa, value), gdzie X to nazwa typu. Po skonczeniu modyfikowania wywołujemy metodę ResultSet.updateRow() (lub ResultSet.cancelUpdates()). danych 20 / 29
Modyfikowanie relacji wynikowych przykład Statement stmt = conn. createstatement ( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE) ; ResultSet rs = stmt. executequery ( " s e l e c t nazwisko from osoby " ) ; while ( rs. next ( ) ) { i f ( rs. g e t S t r i n g ( 1 ). equals ( " Kowalski " ) ) { rs. updatestring ( 1, " Kwiatkowski " ) ; rs. updaterow ( ) ; } Uwaga. Te instrukcje zmienia wszystkich Kowalskich w tabeli osoby na Kwiatkowskich. danych 21 / 29
Wstawianie krotek przykład rs. movetoinsertrow ( ) ; rs. updatestring ( 1, " Jan " ) ; rs. updatestring ( 2, " Kowalski " ) ; rs. insertrow ( ) ; rs. movetocurrentrow ( ) ; danych 22 / 29
Przetwarzanie wsadowe przykład Przetwarzanie wsadowe dostępne jest tylko dla UPDATE, INSERT, DELETE. Zapytanie, które generuje wynik spowoduje zgłoszenie wyjatku. PreparedStatement s t m t =conn. preparestatement ( " update osoby set hobby=? where rok_urodzenia=? " ) ; stmt. s e t S t r i n g ( 1, " brak " ) ; stmt. s e t I n t ( 2, 2 0 1 2 ) ; stmt. addbatch ( ) ; stmt. s e t S t r i n g ( 1, " kaszka " ) ; stmt. s e t I n t ( 2, 2 0 1 1 ) ; stmt. addbatch ( ) ; i n t [ ] t = stmt. executebatch ( ) ; danych 23 / 29
Modyfikowanie ilości przesyłanych krotek Sterownik JDBC dla Oracle umozliwia zwiększenie liczby przeysłanych krotek metoda setrowprefetch(int). Musimy zrzutować obiekt Statement na OracleStatement. Mechanizm jest podobny dla PreparedStatement i OraclePreparedStatement. Pozwala to zwiększenie efektywności współpracy z BD. danych 24 / 29
Modyfikowanie ilości przesyłanych krotek przykład Statement stmt=conn. createstatement ( ) ; ( ( OracleStatement ) stmt ). setrowprefetch ( 1 0 ) ; ResultSet rs = stmt. executequery ( " s e l e c t imie, nazwisko from osoby " ) ; while ( rs. next ( ) ) System. out. p r i n t l n ( rs. g e t S t r i n g (1)+ " " + rs. g e t S t r i n g ( 2 danych 25 / 29
Interfejs dla C++ OCCI OCCI Oracle C++ Call Interface, Oferuje podobna funkcjonalność co JDBC. Pobieramy ze strony Oracle. Trzeba uważać, żeby pobrać właściwa wersję, tzn. właściw a dla kompilatora i właściwa dla platformy na która piszemy aplikację (32 lub 64 bit). Pobieramy i rozpakowujemy InstantClient. Dodajemy ścieżki do katalogów, w których trzymamy instantclient-a oraz biblioteki dla Visual Studio XXXX. danych 26 / 29
Interfejs dla C++ OCCI Piszac aplikację określamy w projekcie: miejsce dla plików z definicjami: w zakładce VC++ Directories/ Include Directories dopisujemy ścieżkę Oracle, np. c:/oraclexe/app/oracle/product/11.2.0/serve/oci/include do Library Directories dodajemy katalogi z bibliotekami, w których zainstalowaliśmy instantclient oraz biblioteki Visual Studio (vc10,...). danych 27 / 29
Program porównawczy # i n c l u d e " occi. h " # i n c l u d e <iostream > # i n c l u d e <iomanip > namespace oc = o racle : : o cci ; using namespace System ; using namespace std ; oc : : Environment env ; oc : : Connection conn ; oc : : Statement stmt = NULL ; oc : : ResultSet rs = NULL ; s t r i n g s q l = " s e l e c t imie, nazwisko from osoby " ; danych 28 / 29
Program porównawczy i n t main ( ) { env=oc : : Environment : : createenvironment ( oc : : Environment : : DEFAULT ) ; conn = env >createconnection ( " user ", " pswd ", "XE" ) ; stmt = conn >createstatement ( s q l ) ; rs = stmt >executequery ( ) ; i f ( rs ) { while ( rs >next ( ) ) { cout << setw ( 16) << l e f t << rs >g e t S t r i n g ( 1 ) ; } } stmt >closeresultset ( rs ) ; conn >terminatestatement ( stmt ) ; env >terminateconnection ( conn ) ; oc : : Environment : : terminateenvironment ( env ) ; return 0; Konrad } Zdanowski ( Uniwersytet Kardynała Stefana Bazy danych Wyszyńskiego, wykładwarszawa) danych 29 / 29