background image

 

 

Plan wykładu

1. Zaawansowane możliwości JDBC:

 rodzaje obiektów 

ResultSet

,

 dodatkowe możliwości obiektów 

ResultSet

,

 zapytania prekompilowane,

 wywoływanie zdalnych procedur,

 transakcje.

2. Przegląd wybranych systemów bazodanowych:

 HSQLDB.

1

background image

 

 

Rodzaje obiektów ResultSet

Ze względu na dostęp do odebranych danych obiekty 

ResultSet

 dzielimy na:

 

TYPE_FORWARD_ONLY

 – odbiór danych kolejno od pierwszego do ostatniego 

rekordu,

 

TYPE_SCROLL_INSENSITIVE

 – dostęp do dowolnych danych, przygotowane 

wyniki nie zmieniają sie pod wpływem zmian w bazie.

 

TYPE_SCROLL_SENSITIVE

 – dostęp do dowolnych danych, przygotowane wyniki 

zmieniają sie pod wpływem zmian w bazie. Kolejność rekordów nie musi być stała.

Rodzaj dostępu zmieniamy metodą 

setFetchDirection(int)

. Do przechodzenia 

między rekordami służą metody: 

next()

previous()

last()

first()

absolute()

relative()

.

2

background image

 

 

Rodzaje obiektów ResultSet

Obiekty 

ResultSet

 mogą mieć różne możliwości zmieniana odebranych danych:

1. 

CONCUR_READ_ONLY

 – dane nie mogą być zmienione poprzez metody 

updateXXX()

 najwyższy poziom współbieżności (największa liczba użytkowników jednocześnie 

operujących na danych

 Jedyna możliwość w wersji JDBC 1.0

2. 

CONCUR_UPDATABLE

 – dane mogą być zmieniane.

 zmniejszony poziom współbieżności,

 mniejsza wydajność.

3

background image

 

 

Rodzaje obiektów ResultSet

Connection con = DriverManager.getConnection(              

"jdbc:my_subprotocol:my_subname");

Statement stmt = con.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,

ResultSet.CONCUR_UPDATABLE,

ResultSet.HOLD_CURSORS_OVER_COMMIT);

stmt.setFetchSize(25);

ResultSet rs = stmt.executeQuery(

"SELECT col1, col2 FROM table1");

Obiekt 

rs

 udostępnia dane w dowolnej kolejności, umożliwia zmianę danych, nie 

jest zamykany przy zatwierdzeniu transakcji. Do bazy danych zostaje przekazana 

sugestia, aby dane odbierać w pakietach po 25 rekordów.

4

background image

 

 

Inne operacje na obiektach ResultSet

1. Usuwanie rekordów:

rs.first();

rs.deleteRow();

2. Wstawianie rekordów:

rs.moveToInsertRow();

rs.updateObject(1, myArray);

rs.updateInt(2, 3857);

rs.updateString(3, "Mysteries");

rs.insertRow();

rs.first();

5

background image

 

 

Inne operacje na obiektach ResultSet

Uwagi do wstawiania rekordów:

1. Na wstawianym rekordzie można wywoływać metody 

getXXX()

. Jeśli 

odpowiednia wartość nie została ustawiona wcześniej metodą 

updateXXX()

 

wartość zwracana będzie nieokreślona.

2. Aktualizacja wartości we wstawianym rekordzie nie zmienia obiektu 

ResultSet

.

3. metoda 

insertRow()

, dodająca rekord do obiektu 

ResultSet

 i do bazy danych 

zrzuca 

SQLException

, jeśli liczba lub typy kolumn nie zgadzają się ze specyfikacją 

tabeli w bazie.

4. Bieżącym rekordem jest ten, który był nim przed wywołaniem metody 

moveToInsertRow()

.

6

background image

 

 

Inne operacje na obiektach ResultSet

Odczytywanie dużych porcji danych:

Statement stmt = con.createStatement();

ResultSet rs = stmt.executeQuery("SELECT xdata FROM Table2");

byte [] buff = new byte[4096];

while (rs.next()) {

    InputStream fin = rs.getAsciiStream(1);

    for (;;) {

        int size = fin.read(buff);

        if (size == -1) break;

        // wypisanie danych

        System.out.write(buff, 0, size);

    }

}

7

background image

 

 

Prekompilowane zapytania

JDBC przewiduje możliwość tworzenia prekompilowanych zapytań. Służy do tego 

klasa 

PreparedStatement

 wyprowadzona z klasy 

Statement

. Przykłady:

PreparedStatement pstmt = con.prepareStatement(

                            "UPDATE table4 SET m = ? WHERE x = 
?");

PreparedStatement pstmt2 = con.prepareStatement(

      "SELECT a, b, c FROM Table1", 
ResultSet.TYPE_SCROLL_SENSITIVE,

      ResultSet.CONCUR_UPDATABLE);

ResultSet rs = pstmt2.executeQuery();

Używanie prekompilowanych zapytań może zwiększyć szybkość działania 

programu.

8

background image

 

 

Przekazywanie parametrów

Przekazywanie parametrów:

pstmt.setString(1, "Hi"); 

for (int i = 0; i < 10; i++) {

    pstmt.setInt(2, i);

    int rowCount = pstmt.executeUpdate();

}

Od wersji JDBC 2.0 można przekazywać parametry typu 

SQL BLOB

 oraz 

SQL 

ARRAY

.

PreparedStatement pstmt = con.prepareStatement(

"UPDATE Table3 SET Stats = ? WHERE Depts = ?");

pstmt.setBlob(1, statistics);

pstmt.setArray(2, departments);

9

background image

 

 

Przekazywanie dużych parametrów

Przykład pokazuje jak przesłać zawartość pliku jako parametr wejściowy:

File file = new File("/tmp/data");

int fileLength = file.length();

InputStream fin = new FileInputStream(file);

PreparedStatement pstmt = con.prepareStatement(

"UPDATE Table5 SET stuff = ? WHERE index = 4");

pstmt.setBinaryStream (1, fin, fileLength);

pstmt.executeUpdate();

Inny sposób polega na wykorzystaniu typów BLOB i CLOB. 

10

background image

 

 

Informacje o parametrach

Dodatkowe dane o parametrach prekompilowanego zapytania można uzyskać 

poprzez interfejs 

ParameterMetaData

 (JDBC 3.0):

PreparedStatement pstmt = con.prepareStatement(

"INSERT INTO QUOTAS (ID, LAST, FIRST, DEPT, QUOTA) " +

"VALUES (?, ?, ?, ?, ?)";

ParameterMetaData paramInfo = pstmt.getParameterMetaData();

int numberOfParams = paramInfo.getParameterCount();

for (int i = 1; i <= numberOfParams; i++) {

String dbType = paramInfo.getParameterTypeName(i);

System.out.println("Param " + i " is DBMS type " + dbType);

}

11

background image

 

 

Serie prekompilowanych zapytań

Podobnie jak w przypadku 

Statement

 istnieje możliwość przesłania serii zapytań.

PreparedStatement pstmt = con.prepareStatement(

             "UPDATE Table4 SET History = ? WHERE ID = ?");

pstmt.setClob(1, clob1);

pstmt.setLong(2, 350985839);

pstmt.addBatch();

pstmt.setClob(1, clob2);

pstmt.setLong(2, 350985840);

pstmt.addBatch();

int [] updateCounts = pstmt.executeBatch();

Jeśli którekolwiek z zapytań 

UPDATE

 zwróci cokolwiek ponad liczbę zmienionych 

rekordów metoda 

executeBatch()

 zrzuci wyjątek.

12

background image

 

 

Zdalne procedury

Do wywoływania zdalnych procedur używa się obiektów klasy 

CallableStatement

:

CallableStatement cstmt = con.prepareCall(

"{call updatePrices(?, ?)}");

cstmt.setString(1, "Colombian");

cstmt.setFloat(2, 8.49f);

cstmt.addBatch();

cstmt.setString(1, "Colombian_Decaf");

cstmt.setFloat(2, 9.49f);

cstmt.addBatch();

int [] updateCounts = cstmt.executeBatch();

Procedura zostanie wywołana dwukrotnie Parametry przekazywane do procedury 

nazywamy parametrami IN.

13

background image

 

 

Zdalne procedury – odbieranie wyników

Istnieje możliwość ustawienia parametrów przez zdalną procedurę (parametry OUT):

CallableStatement cstmt = con.prepareCall(

"{call getTestData(?, ?)}");

cstmt.registerOutParameter(1, java.sql.Types.TINYINT);

cstmt.registerOutParameter(2, java.sql.Types.DECIMAL);

ResultSet rs = cstmt.executeQuery();

// ... odczyt danych poprzez ResultSet

byte x = cstmt.getByte(1); // odczyt zwracanych parametrow

BigDecimal n = cstmt.getBigDecimal(2);

Procedura wypełnia przekazywane parametry.

14

background image

 

 

Zdalne procedury – odbieranie wyników

Numeracja parametrów OUT:

CallableStatement cstmt = con.prepareCall(

                          "{call getTestData(25, ?)}");

cstmt.registerOutParameter(1, java.sql.Types.TINYINT);

Przy numeracji uwzględniane są tylko parametry oznaczone znakiem zapytania

15

background image

 

 

Zdalne procedury – parametry INOUT

Parametry INOUT to takie, które są przekazywane do procedury a następnie 

modyfikowane przez wywołaną procedurę.

CallableStatement cstmt = con.prepareCall(

                              "{call reviseTotal(?)}");

cstmt.setByte(1, (byte)25);

cstmt.registerOutParameter(1, java.sql.Types.TINYINT); 

cstmt.executeUpdate();

byte x = cstmt.getByte(1);

Parametr jest najpierw ustawiany – IN a następnie rejestrowany jako OUT. Po 

wywołaniu procedury można odebrać jego nową wartość. Zaleca się odebranie 

wszystkich danych poprzez obiekt 

ResultSet

 

przed

 odebraniem parametrów 

INOUT.

16

background image

 

 

Transakcje

Transakcje to zbiór operacji zgrupowanych w jednym lub wielu obiektach 

Statement

. Aby zakończyć transakcję należy wywołać metodę 

commit()

 na rzecz 

obiuektu 

Connection

. Domyślnie metoda 

commit()

 jest wywoływana po 

zakończeniu wykonywania zapytań w ramach jednego obiektu 

Statement

. Aby to 

zmienić należy użyć metody 

setAutoCommit(false)

. Do anulowania zmian 

wprowadzonych przez niezatwierdzoną transakcję służy metoda 

rollback()

17

background image

 

 

Poziomy izolacji

Zwykle w systemy baz danych realizują jednocześnie wiele transakcji. Aby 

zapewnić kontrolę nad tym procesem wprowadzono tzw. poziomy izolacji, poprzez 

które określa się zasady równoległej realizacji kilku transakcji. JDBC przewiduje 

pięć poziomów izolacji:

TRANSACTION_NONE

 – brak transakcji.

TRANSACTION_READ_UNCOMMITTED

 – dopuszcza odczyt danych przed 

wywołaniem metody 

commit()

.

TRANSACTION_READ_COMMITTED

 – inne transakcje nie mogą odczytywać 

zmienionych wierszy przed wywołaniem metody 

commit()

 (dirty reads).

18

background image

 

 

Poziomy izolacji

TRANSACTION_REPEATABLE_READ

 – dodatkowo chroni przed sytuacją gdy 

transakcja odczytuje wiersz, druga transakcja go zmienia a pierwsza ponownie go 

odczytuje otrzymując inne dane (non-repetable reads).

TRANSACTION_SERIALIZABLE

 – dodatkowo chroni przed sytuacją, gdy jedna 

transakcja odczytuje zbiór wierszy spełniający kryteria zawarte w warunku 

WHERE

następnie druga transakcja wstawia wiersz spełniający ten warunek, po czym 

pierwsza transakcja ponownie odczytuje zbiór wierszy dostając nowy rekord 

(phantom-read).

Poziomy izolacji ustawia się metodą 

setTransactionIsolation(int)

 

wywołaną na rzecz obiektu klasy 

Connection

.

19

background image

 

 

Etapy transakcji - Savepoints

Obiekt 

Savepoint

 (JDBC 3.0) umożliwia częściowe odwrócenie (rollback

transakcji zamiast całkowitego. Do utworzenia tego obiektu służy metoda 

setSavepoint()

.

Statement stmt = con.createStatement();

int rows = stmt.executeUpdate("INSERT INTO AUTHORS VALUES " +

       "(LAST, FIRST, HOME) 'TOLSTOY', 'LEO', 'RUSSIA'");

Savepoint save1 = con.setSavepoint("SAVEPOINT_1");

int rows = stmt.executeUpdate("INSERT INTO AUTHORS VALUES " +

       "(LAST, FIRST, HOME) 'MELVOY', 'HAROLD', 'FOOLAND'");

...

con.rollback(save1);

...

con.commit();

20

background image

 

 

Zewnętrzne bazy danych

Interfejs JDBC jest dostarczany wraz z większością znanych systemów 

bazodanowych. W dalszej części wykładu zostaną zaprezentowane dwa przykłady 

systemów baz danych od strony ich wykorzystania z programami w języku Java.

Pierwszy z nich to natywna baza danych dla Javy : HSQLDB, drugi to jedna z 

najpowszechniej stosowanych baz danych w małych i średnich projektach: MySQL.

21

background image

 

 

HSQLDB

22

HSQLDB to system baz danych w całości napisany w Javie (open source)..

Strona domowa projektu: http://www.hsqldb.org.

Niektóre własności:

 obsługa SQL'a,

 transakcje (COMMIT, ROLLBACK, SAVEPOINT),

 integralność relacji (klucze obce), operacje kaskadowe,

 zdalne procedury i funkcje (pisane w Javie),

 triggery,

 możliwość dołączania do programów i appletów. Działanie w trybie read-only,

 tabele o rozmiarze do 8GB,

 rozmiar tekstowych i binarnych danych ograniczony przez rozmiar pamięci.

background image

 

 

HSQLDB – tryby pracy serwera

23

 Hsqldb Server – tryb preferowany. Klasa  

org.hsqldb.Server

,

 Hsqldb Web Server – używany, jeśli serwer może używać tylko 

protokołu

 HTTP 

lub HTTPS. W przeciwnym razie niezalecany. Klasa 

org.hsqldb.WebServer

,

 Hsqldb Servlet – używa tego samego protokołu co Web Server. Wymaga osobnego 

kontenera serwletów (np. Tomcat). Może udostępniać tylko jedną bazę danych.

Wszystkie tryby pracy serwera umożliwiają korzystanie z JDBC.

background image

 

 

HSQLDB – połączenie z serwerem

24

Przykład:

try {

Class.forName("org.hsqldb.jdbcDriver").newInstance();

} catch (Exception e) {

System.out.println("ERROR");

e.printStackTrace();

return;

}

Connection c = DriverManager.getConnection(

"jdbc:hsqldb:hsql://localhost/xdb", "sa", "");

Serwer: 

localhost

, nazwa bazy: 

xdb

, użytkownik: 

sa

, hasło: 

''

.

background image

 

 

HSQLDB – tryb stand-alone

25

W trybie stand-alone „serwer” bazy danych działa w ramach tej samej wirtualnej 

maszyny Javy, co korzystający z niego program „kliencki”. Przykład uruchomienia 

bazy:

Connection c = DriverManager.getConnection(

"jdbc:hsqldb:file:/opt/db/testdb", "sa", "");

Niewielkie bazy danych mogą być uruchamiane do pracy w pamięci operacyjnej 

komputera:

Connection c = DriverManager.getConnection(

"jdbc:hsqldb:mem:testdb", "sa", "");

W obecnej wersji HSQLDB istnieje możliwość jednoczesnego używania wielu 

„serwerów” baz danych działających w trybie stand-alone.

background image

 

 

HSQLDB – kończenie pracy z bazą

26

Wszystkie bazy mogą być zamknięte komendą SQL 

SHUTDOWN

. Parametr połączenia 

shutdown=true

 wymusza „zamknięcie” bazy danych wraz z zakończeniem 

ostatniego połączenia za pomocą metody 

close()

.

Po komendzie 

SHUTDOWN

 wszystkie aktywne transakcje są anulowane (rollback

Komenda 

SHUTDOWN COMPACT

 dodatkowo przepisuje pliki .data zmniejszając ich 

rozmiar.

background image

 

 

Podsumowanie

27

Korzystanie z interfejsu JDBC umożliwia jednolity sposób dostępu do różnych 

systemów bazodanowych. Jako przykład przedstawiono bazę HSQLDB. Jej ważną 

zaletą jest możliwość działania w ramach jednej Wirtualnej Maszyny Javy wraz z 

programem klienckim.