background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

jdbc@eng.sun.com

.

8

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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.

background image

 

 

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()

.

background image

 

 

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

background image

 

 

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

background image

 

 

Odbieranie danych

21

Więcej informacji o typach danych: 

http://java.sun.com/j2se/1.4.2/docs/guide/jdbc/getstart/mapping.html#1033804

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

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.