Java Database Connectivity
Java Database Connectivity (JDBC) to specyfikacja określająca zbiór klas
i interfejsów napisanych w Javie, które mogą być wykorzystane przez
programistów tworzących oprogramowanie korzystające z baz danych.
Implementacja JDBC jest dostarczany przez producentów baz danych.
Jedną z ważniejszych zalet takiego rozwiązania jest ukrycie przed programistą
kwestii technicznych dotyczących komunikacji z bazą danych. Dzięki temu ten
sam program napisany w Javie może współpracować z różnymi systemami baz
danych (np. Oracle, Sybase, IBM DB2). Wystarczy podmienić odpowiednie
biblioteki implementujące JDBC.
Źródło informacji:
http://java.sun.com/j2se/1.4.2/docs/guide/jdbc/getstart/GettingStartedTOC.fm.html
1
Bazy danych w Javie: przykład MySQL
import
java.sql.Connection;
import
java.sql.DriverManager;
import
java.sql.ResultSet;
import
java.sql.SQLException;
import
java.sql.Statement;
// Nie importujemy pakietu com.mysql.jdbc.*
public
class
TestDriver {
public
static
void
main(String[] args) {
Statement stmt =
null
;
ResultSet rs =
null
;
try
{
// newInstance() jest wywoływane na wszelki wypadek
Class.forName(
"com.mysql.jdbc.Driver"
).newInstance();
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost/test?user=monty&password="
);
stmt = con.createStatement();
rs = stmt.executeQuery(
"SELECT * FROM tabela"
);
while
(rs.next()) {
System.
out
.println(rs.getString(
"kolumna1"
));
}
}
2
Bazy danych w Javie: przykład MySQL
catch
(Exception ex) {
// obsluga bledow
}
finally
{
// zwalnianie zasobow
if
(rs !=
null
) {
try
{
rs.close();
}
catch
(SQLException sqlEx) {
// ignorujemy
}
rs =
null
;
}
if
(stmt !=
null
) {
try
{
stmt.close();
}
catch
(SQLException sqlEx) {
// ignorujemy
}
stmt =
null
;
}
// analogicznie con.close();
}
}
}
3
JDBC a inne rozwiązania
ODBC (Open DataBase Connectivity) – najpopularniejszy mechanizm dostępu do
relacyjnych baz danych. Dlaczego zatem nie używać ODBC w Javie?
1. ODBC używa interfejsu języka C. Wywołania z Javy kodu natywnego w C
powoduje wiele problemów - bezpieczeństwo, efektywność, przenośność.
2. Bezpośrednie przepisanie interfejsu ODBC w Javie byłoby nieefektywne.
3. ODBC jest stosunkowo trudne w opanowaniu, ponieważ trzeba ustawiać wiele
parametrów nawet przy wywołaniu prostych zapytań.
4. Driver ODBC musi być ręcznie instalowany i konfigurowany na każdym
komputerze klienckim.
Podobne problemy wykluczają stosowanie innych technologii: OLE (Object
Linking and Embedding) DB, ADO (ActiveX Data Objects), and RDS (Remote
Data Service). Niedawno Microsoft wprowadził mechanizm UDA (Universal Data
Access), który jest dosyć podobny do JDBC.
4
Model dwu- i trzywarstwowy komunikacji
5
aplikacja
baza danych
JDBC
protokół
bazy danych
klient
serwer
aplikacja, applet
lub
przeglądarka WWW
serwer aplikacji
JDBC
baza danych
HTTP, RMI, CORBA
lub inne wywołanie
protokół
bazy danych
klient - GUI
serwer
logika
biznesowa
Typy sterowników JDBC
1. JDBC-ODBC bridge plus ODBC driver: konwertuje żądania JDBC na ODBC,
które jest używane do komunikacji z bazą. Najczęściej wykorzystywany w dużych
sieciach korporacyjnych lub modelu trzywarstwowym.
2. Native-API partly-Java driver: konwertuje żądania JDBC do innego natywnego
klienckiego API dla bazy danych.
3. JDBC-Net pure Java driver: konwertuje żądania JDBC na operacje niezależnego
protokołu. Te wywołania są tłumaczone przez warstwę pośrednią na konkretny
protokół systemu baz danych.
4. Native-protocol pure Java driver: konwertuje żądania JDBC na operacje
niezależnego protokołu konkretnego systemu baz danych. To jest najbardziej
wydajne rozwiązanie.
Informacje o sterownikach JDBC:
http://servlet.java.sun.com/products/jdbc/drivers
6
Nawiązanie połączenia
Istnieją dwie metody nawiązania połączenia z bazą danych:
●
za pomocą klasy
DriverManager
:
String url = "jdbc:odbc:bazadanych";
Connection con = DriverManager.
getConnection(url, "login", "haslo");
●
za pomocą klasy
DataSource
i usługi JNDI:
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/MojaDB");
Connection con = ds.getConnection("myLogin", "myPassword");
7
DriverManager
DriverManager
jest tradycyjną warstwą zarządzającą JDBC pomiędzy
użytkownikiem a sterownikiem. Aktualną listę dostępnych sterowników można
uzyskać za pomocą metody:
Enumeration DriverManager.getDrivers()
.
Aby załadować dodatkowy sterownik należy skorzystać z metody:
Class Class.forName(String)
podając jako argument klasę ze sterownikiem
np:
Class.forName("jdbc.odbc.JdbcOdbcDriver"); // protokół
jdbc:odbc
Class.forName("com.mysql.jdbc.Driver"); //protokół jdbc:mysql
.
Rejestracja podprotokołów:
8
DriverManager
Inne metody (statyczne) udostępniane przez DriverManager'a:
●
int getLoginTimeout()
,
setLoginTimeout(int)
– pobiera/ustawia czas
w sekundach określający okres oczekiwania na zalogowanie się do bazy.
●
PrintWriter getLogWriter()
,
void setLogWriter(PrintWriter)
–
pobiera/ustawia obiekt do zapisywania logów,
●
void println(String message)
– wstawia komunikat do logu,
●
void registerDriver(Driver driver)
,
void deregisterDriver(Driver)
– rejestruje/wyrejestrowuje sterownik.
Rejestracja powinna odbywać się automatycznie przy załadowaniu sterownika –
kod statyczny w klasie sterownika.
9
DataSource
DataSource
reprezentuje źródło danych. Zawiera informacje identyfikujące i
opisujące dane. Obiekt
DataSource
współpracuje z technologią Java Naming and
Directory Interface (JNDI), jest tworzony i zarządzany niezależnie od używającej go
aplikacji.
Korzystanie ze źródła danych zarejestrowanego w JNDI zapewnia:
●
brak bezpośredniego odwołania do sterownika przez aplikację,
●
umożliwia implementację grupowania połączeń (pooling) oraz rozproszonych
transakcji.
Te cechy sprawiają, że korzystanie z klasy
DataSource
jest zalecaną metodą
tworzenia połączenia z bazą danych, szczególnie w przypadku dużych aplikacji
rozproszonych.
10
Standardowe własności DataSource
W wersji 2.0 JDBC określono zbiór własności, które musi posiadać
DataSource
:
●
databaseName
- nazwa bazy danych na serwerze bazy danych,
●
dataSourceName
– nazwa logiczna źródła danych; używana tylko gdy
implementowany jest pooling i rozproszone transakcje,
●
description
– opis źródła danych,
●
networkProtocol
– protokół sieciowy używany do komunikacji z serwerem
(
String
),
●
portNumber
– numer portu, poprzez który dostajemy się do serwera bazy
danych (
int
),
●
user
i
password
– login i hasło,
●
serverName
– nazwa serwera lub adres IP
11
Rejestracja źródła danych w JNDI
Przed użyciem obiektu
DataSource
powinien on zostać zarejestrowany:
VendorDataSource vds = new VendorDataSource();
vds.setServerName("moj_serwer");
vds.setDatabaseName("nazwa_mojej_bazy");
vds.setDescription("tutaj_kroki_opis");
Context ctx = new InitialContext();
ctx.bind("jdbc/MojaDB", vds);
Klasa
VendorDataSource
jest dostarczana przez producenta systemu bazy danych,
w ramach sterownika JDBC.
UWAGA: nazwa
VendorDataSource
jest przykładowa.
12
Przesyłanie zapytań
Do przesyłania zapytań do bazy danych służą obiekty klasy:
●
Statement
– typowe pytania (bezparametrowe),
●
PreparedStatement
– prekompilowany pytania zawierające parametry
wejściowe,
●
CallableStatement
– procedury zapisane w bazie danych.
Obiekt
Statement
tworzy się w ramach nawiązanego wcześniej połączenia:
Connection con = DriverManager.getConnection(url, login,
pass);
Statement stmt = con.createStatement();
stmt.executeUpdate("INSERT INTO table(name, price) VALUE
'ser', 2.0");
13
Przesyłanie zapytań
Obiekt Statement zawiera Do przesyłania zapytań służą metody:
●
executeQuery
– pytania zwracające dane:
SELECT
,
●
executeUpdate
– pytania zmieniające dane:
INSERT
,
UPDATE
,
CREATE
TABLE
, ... .
●
execute
– dowolne zapytania. Dzięki tej instrukcji można wykonać sekwencje
pytań, przekazać i odebrać dodatkowe parametry.
W ramach jednego obiektu
Statement
można wykonać sekwencyjnie kilka
zapytań. Po zakończeniu używania obiektu zaleca się wywołanie metody
close()
.
14
Odbieranie generowanych kluczy
Do odbioru automatycznych kluczy służy metoda
getGeneratedKeys()
wywołana na rzecz obiektu
Statement
:
Statement stmt = con.createStatement();
String sql =
"INSERT INTO AUTHORS(LAST, FIRST, HOME) VALUES "
+
"('PARKER', 'DOROTHY', 'USA')"
;
int
rows = stmt.executeUpdate(sql,
Statement.
RETURN_GENERATED_KEYS
);
ResultSet rs = stmt.getGeneratedKeys();
if
(rs.next()) {
ResultSetMetaData rsmd = rs.getMetaData();
int
colCount = rsmd.getColumnCount();
do
{
for
(
int
i = 1; i <= colCount; i++) {
String key = rs.getString(i);
System.
out
.println(
"klucz "
+ i +
": "
+ key);
}
}
while
(rs.next());
}
else
{
System.
out
.println(
"Brak automatycznych kluczy."
);
}
}
15
Specjalne parametry
Parametry specjalne mają następującą postać:
{keyword
. . .
parameters
. . .
}
.
Używa się następujących słów kluczowych:
●
escape
- przesyłanie znaków w klauzuli
LIKE
:
stmt.executeQuery("SELECT name FROM Identifiers WHERE Id
LIKE '\_%' {escape '\'}"); - Id zaczyna się od '_'
●
fn
– funkcje skalarne:
{fn concat("Hot", "Java")};
{fn user()};
Listę wspieranych funkcji można uzyskać za pomocą metod obiektu
DatabaseMetaData
, np:
getNumericFunctions()
,
getStringFunctions()
, ... .
16
Specjalne parametry
Parametry specjalne mają następującą postać:
{keyword
. . .
parameters
. . .
}
.
Używa się następujących słów kluczowych:
●
escape
- przesyłanie znaków w klauzuli
LIKE
:
stmt.executeQuery("SELECT name FROM Identifiers WHERE Id
LIKE '\_%' {escape '\'}"); - Id zaczyna się od '_'
●
fn
– funkcje skalarne:
{fn concat("Hot", "Java")};
{fn user()};
Listę wspieranych funkcji można uzyskać za pomocą metod obiektu
DatabaseMetaData
, np:
getNumericFunctions()
,
getStringFunctions()
, ... .
The driver will either map the escaped function call into the appropriate syntax
or implement the function directly itself. However, a driver is required to implement
only those scalar functions that the DBMS supports.
* d, t, and ts for date and time literals
DBMSs differ in the syntax they use for date, time, and timestamp literals. The
JDBC API supports ISO standard format for the syntax of these literals, using an
escape clause that the driver must translate to the DBMS representation. For
example, a date is specified in a JDBC SQL statement with the following syntax:
{d 'yyyy-mm-dd'}
In this syntax, yyyy is the year, mm is the month, and dd is the day. The driver
will replace the escape clause with the equivalent DBMS-specific representation. For
example, the driver might replace {d 1999-02-28} with '28-FEB-99' if that is the
appropriate format for the underlying database.
There are analogous escape clauses for TIME and TIMESTAMP:
{t 'hh:mm:ss'}
{ts 'yyyy-mm-dd hh:mm:ss.f . . .'}
The fractional seconds (.f . . .) portion of the TIMESTAMP can be omitted.
* call or ? = call for stored procedures
If a database supports stored procedures, they can be invoked from JDBC with
the syntax shown below. Note that the square brackets ([ ]) indicate that what is
between them is optional, and they are not part of the syntax.
{call procedure_name[(?, ?, . . .)]}
or, where a procedure returns a result parameter:
{? = call procedure_name[(?, ?, . . .)]}
Input arguments may be either literals or parameters. See the section "Numbering
of Parameters" on page 103 for more information.
One can call the method DatabaseMetaData.supportsStoredProcedures to see if
the database supports stored procedures.
* oj forouter joins
The syntax for an outer join is:
{oj outer-join}
In this syntax, outer-join has the form
table {LEFT|RIGHT|FULL} OUTER JOIN {table | outer-join}
ON search-condition
(Note that curly braces ({}) in the preceding line indicate that one of the items
17
●
d
,
t
oraz
ts
– data i czas:
{d 'yyyy-mm-dd'}
{d 1999-02-28}
zostanie zamienione np. na
'28-FEB-99'
jeśli taki format jest
używany przez bazę danych. Analogicznie dla
TIME
i
TIMESTAMP
:
{t 'hh:mm:ss'}
{ts 'yyyy-mm-dd hh:mm:ss.f . . .'}
Część ułamkowa sekund (
.f...
) może zostać opuszczona.
●
call
lub
?=
- wywołanie procedur zapisanych w bazie danych:
{call procedure_name[(?, ?, . . .)]}
lub jeśli procedura zwraca wunik:
{? = call procedure_name[(?, ?, . . .)]}
DatabaseMetaData.supportsStoredProcedures()
mówi, czy baza wspiera
procedury.
Specjalne parametry
18
●
oj
– złączenia zewnętrzne (OUTER JOIN):
{oj outer-join}
gdzie
outer-join
ma postać:
tabela {LEFT|RIGHT|FULL} OUTER JOIN {tabela | outer-join}
ON warunek
Przykład:
Statement stmt = con.createStatement("SELECT * FROM
{oj TABLE1 LEFT OUTER JOIN TABLE2 ON DEPT_NO = 003420930}");
Obiekt
DatabaseMetaData
posiada trzy metody pozwalające stwierdzić, czy baza
wspiera złączenia zewnętrzne:
supportsOuterJoins()
,
supportsFullOuterJoins()
oraz
supportsLimitedOuterJoins()
.
Przesyłanie serii zapytań
try
{
// przygotowanie zapytań
stmt.addBatch(
"INSERT INTO table1 VALUES (1000)"
);
stmt.addBatch(
"INSERT INTO table2 VALUES ('cos')"
);
stmt.addBatch(
"INSERT INTO table3 VALUES ('260')"
);
// wywołanie
int
[] updateCounts = stmt.executeBatch();
}
catch
(BatchUpdateException b) {
int
[] updateCounts = b.getUpdateCounts();
}
// wypisanie liczby zmian
System.out.println(
"Update counts: "
);
for
(
int
i = 0; i < updateCounts.length; i ++) {
System.out.print(updateCounts[i] +
" "
);
}
System.out.println(
""
);
19
Odbieranie danych
Wyniki zwrócone w wyniku wykonania zapytania są dostępne poprzez obiekt typu
ResultSet
. Przykład:
Statement
stmt
= con.createStatement();
ResultSet
rs
=
stmt
.executeQuery(
"SELECT a, b, c FROM table"
);
while
(rs.next()) {
// odebranie i wypisanie wyników w bieżącym rekordzie
int
i = rs.getInt(
"a"
);
String s = rs.getString(
"b"
);
float
f = rs.getFloat(
"c"
);
System.out.println(
"ROW = "
+ i +
" "
+ s +
" "
+ f);
}
20
Odbieranie danych
21
Więcej informacji o typach danych:
http://java.sun.com/j2se/1.4.2/docs/guide/jdbc/getstart/mapping.html#1033804
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
Podstawowe klasy służące do interakcji z systemami bazodanowymi są udostępniane
za pośrednictwem obiektów:
Connection
,
Statement
i
ResultSet
. Wszystkie te
klasy należą do pakietu
java.sql
. Klasy rozszerzające funkcjonalność JDBC (np.
DataSource
) znajdują się w pakiecie
javax.sql
.
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.