Java Database Connectivity - users.uj.edu.plusers.uj.edu.pl/~ciesla/java/java_04.pdf ·...

27
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

Transcript of Java Database Connectivity - users.uj.edu.plusers.uj.edu.pl/~ciesla/java/java_04.pdf ·...

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 wypadekClass.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 zasobowif (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: [email protected].

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 IdLIKE '\_%' {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 IdLIKE '\_%' {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 warunekPrzykł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łanieint [] updateCounts = stmt.executeBatch();

} catch(BatchUpdateException b) {int [] updateCounts = b.getUpdateCounts();

}// wypisanie liczby zmianSystem.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 rekordzieint 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.