JDBC cz. 2, HSQLDB.

Transkrypt

JDBC cz. 2, HSQLDB.
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. SSL i bazy danych: przykład 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
HSQLDB – SSL
HSQLDB umożliwia szyfrowanie transmisji. Aby taka transmisja była możliwa
zarówno serwer jak i klient muszą być odpowiednio skonfigurowane. Konfiguracja
serwera:
1. Generowanie certyfikatu serwera:
>keytool -genkey -alias hsqldb -keyalg RSA -validity 30 -keystore
hsqlserver.store
Enter keystore password:
hsqldb
What is your first and last name?
[Unknown]:
localhost
...
Is CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown,
C=Unknown correct?
[no]:
yes
Enter key password for <hsqldb>
(RETURN if same as keystore password):
21
HSQLDB – SSL
Inny sposób generowania klucza – gdy posiadamy certyfikat podpisany przez
zewnętrzne Centrum Autoryzacji”
openssl pkcs8 -topk8 -outform DER -in Xpvk.pem -inform PEM
-out Xpvk.pk8 -nocrypt
openssl x509 -in Xcert.pem -out Xcert.der -outform DER
java DERImport server.store NEWALIAS Xpvk.pk8 Xcert.der
UWAGA: hasło dla klucza musi być takie samo jak hasło dla server.store!
22
HSQLDB – SSL
2. Uruchomienie serwera:
>java
-Djavax.net.ssl.keyStorePassword=hsqldb
-Djavax.net.ssl.keyStore=hsqlserver.store
-cp hsqldb.jar
org.hsqldb.Server
[Server@13c5982]: [Thread[main,5,main]]: checkRunning(false)
entered
...
[Server@13c5982]: Using TLS/SSL-encrypted JDBC
...
[Server@13c5982]: Startup sequence completed in 978 ms.
[Server@13c5982]: 2006-03-19 10:16:28.100 HSQLDB server 1.8.0
is online
23
HSQLDB – SSL
Po stronie klienta należy:
1. Uzyskać certyfikat serwera np:
>keytool -export -keystore server.store -alias hsqldb
-file server.cer
Enter keystore password:
hsqldb
Certificate stored in file <server.cer>
Jeśli nie mamy dostępu do server.store możemy użyć dowolnego narzędzia do
połączenia się z serwerem i odebrania certyfikatu np:
openssl s_client -connect host:port - zakodowany w Base64 certyfikat
pojawi sie na ekranie pomiędzy liniami
-----BEGIN CERTIFICATE----...
----END CERTIFICATE-----.
Ten fragment zapisujemy do pliku server.cer.
24
HSQLDB – SSL
2. Dodać certyfikat serwera jako zaufany:
>keytool -import -trustcacerts -keystore hsqlclient.store -alias hsql
-file hsqlserver.cer
Enter keystore password:
hsqldb
Owner: CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown,
C=Unknown
Issuer: CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown,
C=Unknown
Serial number: 441d1f43
Valid from: Sun Mar 19 10:07:15 CET 2006 until: Tue Apr 18 11:07:15
CEST 2006
Certificate fingerprints:
MD5:
68:A2:33:BA:FA:4B:08:4A:E2:21:DD:E5:F6:7B:E3:8A
SHA1: 59:6A:4D:66:03:C8:D6:B0:D1:4C:0B:1B:30:E2:90:0F:
88:66:EC:40
Trust this certificate? [no]:
yes
Certificate was added to keystore
25
HSQLDB – SSL
JVM musi zostać poinformowana, ze w pliku hsqlclient.store znajdują się
zaufane certyfikaty. Można to zrobić na trzy sposoby:
a) plik hsqlclient.store należy dodać do katalogu z zaufanymi certyfikatami
(w JDK zwykle: JAVA_HOME/jre/lib/security/cacerts),
b) wywołać program kliencki z opcją:
-Djavax.net.ssl.trustStore=/sciezka/do/hsqlclient.store
c) w kodzie programu klienckiego użyć instrukcji:
System.getProperties().put("javax.net.ssl.trustStore",
"/sciezka/do/hsqlclient.store");
26
HSQLDB – SSL
Przykładowy program klienta:
import java.sql.*;
public class HSQLDb {
public static void main(String[] args){
System.getProperties().put("javax.net.ssl.trustStore",
"/sciezka/do/hsqlclient.store");
System.getProperties().put("javax.net.debug","all");
try {
Class.forName("org.hsqldb.jdbcDriver").newInstance();
} catch (Exception e) { e.printStackTrace(); return; }
27
HSQLDB – SSL
try {
Connection con = DriverManager.getConnection(
"jdbc:hsqldb:hsqls://localhost/test", "sa", "");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT NOW()");
rs.next();
System.out.println(rs.getString(1));
con.close();
} catch (SQLException ex) { ex.printStackTrace(); }
}
}
}
28

Podobne dokumenty