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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
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.
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:
''
.
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.
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.
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.