4.
Accesul la baze de date utiliz$nd Java
4.1 Accesul la baze de date ^n lumea non-java
CLI (Call Level Interface) este un set de func]ii pe care un
program le poate apela pentru a se conecta la baza de date,
efectua reg@siri }i modific@ri. Aceste func]ii sunt bazate pe
un SQl dinamic, cu alte cuvinte func]iile permit s@ se
construiasc@ }i execute comenzi SQL care sunt parcurse la
momentul execu]iei, nefiind necesar@ o precompilare
static@.
Atunci c$nd este utilizat CLI ^ntr-un program pentru a
accesa o baz@ de date, este necesar s@ fie instalat pe
calculator driverul potrivit. Driverul prime}te cererile CLI }i
le translateaz@ ^n limbajul nativ de acces la baza de date al
serverului.
Cei mai mul]i furnizori de baze de date asigur@ propriul
CLI, care este diferit de CLI oferit de al]i v$nz@tori. Avantajul
propriului CLI este ca acesta ofer@ facilit@]i care-l fac mai
u}or de folosit pentru baza de date particular@.
Dezavantajul este desigur acela c@ sunt diferite. Asta
^nseamn@ c@ un programator ce are nevoie s@ acceseze
baze de date diferite va trebui sa ^nve]e cum s@ utilizeze
CLI-uri diferite.
ODBC (Open Database Connectivity) este un CLI standard
care permite programului s@ acceseze orice baz@ de date.
Pentru a utiliza ODBC este nevoie de driverul ODBC potrivit;
fiecare baz@ de date utilizeaz@ un driver diferit.
Problema cu ODBC este c@ nu e potrivit pentru Java.
ODBC este o interfa]@ C. %ntr-adev@r, este posibil s@ fie
apelate metode native C dintr-un program Java, dar
consecin]ele sunt c@ programul devine dependent de
56
platforma particular@ pe care driverul ODBC este instalat.
Pierderea portabilit@]ii face ca ODBC sa fie un candidat
nepotrivit pentru dezvoltatorii Java.
4.2 Java Database Connectivity (JDBC)
JDBC (Java Database Connectivity) define}te un set de
clase }i interfe]e ce permit programelor Java s@ manipuleze
o baz@ de date. Clasele din JDBC sunt definite ^n pachetul
[Link].
JDBC este un protocol standard, adic@ clasele }i
interfe]ele definite ^n JDBC sunt standard. Aceasta
^nseamn@ c@ un programator poate scrie programe Java
care s@ utilizeze orice baz@ de date.
Pentru fiecare baz@ de date, pentru a realiza conexiunea
la ea, este nevoie de un driver JDBC diferit. %n func]ie de
cum va fi driverul instalat exist@ dou@ modalit@]i de baz@
de instalare (pentru Oracle):
-se descarc@ driverul JDBC din re]ea. Acest driver este numit
“JDBC thin” }i poate fi utilizat at$t pentru applet-uri c$t }i
pentru aplica]ii;
-instalarea prealabil@ a driverului pe calculator. Acest driver
este cunoscut ca “JDBC OCI”, }i poate fi utilizat doar de
aplica]iile Java.
JDBC joac@ ^n Java un rol similar cu cel al ODBC-ului ^n
lumea non-Java.
Clasele si interfe]ele din JDBC care specific@ metode pe
care un program Java le poate apela pentru a accesa o baz@
de date sunt acelea}i, indiferent de baza de date, adic@
codul nu trebuie schimbat.
Driverul JDBC intercepteaz@ apelurile JDBC }i le
^nainteaz@ bazei de date.
Dup@ cum am amintit mai sus Oracle ofer@ dou@ tipuri
diferite
de drivere, ^n func]ie de aplica]ia care se dezvolt@.
57
Driverele Oracle Thin sunt scrise ^n ^ntregime ^n
Java }i ca urmare sunt independente de platform@. Clasele
incluse ^n aceste drivere JDBC pot sa fie desc@rcate din
re]ea ^mpreun@ cu un applet; nu este nevoie s@ fie instalat
^n prealabil vreun software Oracle pe ma}ina client.
Din punct de vedere tehnic, acest tip de drivere
utilizeaz@ sockets-i pentru a se conecta direct la Oracle si
sunt cunoscute ca drivere de tipul 4.
Driverele Oracle JDBC OCI (Oracle Call Interface) ofer@ o
implementare a interfe]elor JDBC care folosesc OCI pentru a
comunica cu Oracle. %n comunitatea Java DataBase
Connectivity ele sunt cunoscute ca drivere de tipul 2. Acest
drivere apeleaz@ metode native care trebuie s@ fie instalate
^n prealabil pe ma}ina client. Chem$nd aceste metode
native, driverele devin specifice unei platforme.
4.3 SQL dinamic versus SQL static ^n Java
JDBC este prin natura sa dinamic. Construc]iile SQL care
sunt specificate ^n metodele JDBC sunt parsate numai la
momentul execu]iei. Nu exist@ o verificare la compilare,
adic@ construc]iile SQL nu sunt verificate dac@ sunt corecte
sintactic sau dac@ corespund la modelul actual al bazei de
date.
Acest lucru spore}te posibilatatea apari]iei de erori la
momentul execu]iei, cauza fiind ori o comand@ SQL
incorect@ din punct de vedere sintactic, ori o presupunere
eronat@ despre modelul bazei de date.
SQL static este opusul SQL-ului dinamic: exist@
posibilitatea de a include construc]ii SQL ^n programele Java
}i ^nainta programele unui precompilator. Precompilatorul
verific@ sintaxa SQL atunci }i d@ erori daca ea nu este
corect@, ori dac@ nu se potrive}te cu modelul bazei de
date. %n acest fel se reduce num@rul de erori la momentul
execu]iei.
58
4.4 Accesul la baze de date folosind JDBC
JDBC este bazat pe standardul X/Open SQL-2. Rela]ia dintre
clasele JDBC }i baza de date se poate reprezenta astfel:
Clasa DriverManager ofer@ posibilitatea ^nc@rcarii
driverelor JDBC. DriverManager distribuie conexiunile la o
anumit@ surs@ de date prin metoda getConnection().
Clasa Connection gestioneaz@ o sesiune de lucru cu o
baz@ de date. Aceast@ clas@ este utilizat@ pentru a crea
un obiect de tip Statement folosind metoda
createStatement().
Clasa Statement execut@ construc]iile SQL. De exemplu,
reg@sirile pot fi efectuate cu ajutorul metodei
executeQuery(), iar rezultatele sunt ^mpachetate ^ntr-un
obiect de tip ResultSet.
Clasa ResultSet asigur@ accesul la datele generate
execut$nd un obiect Statement. Liniile sunt recep]ionate ^n
secven]@. %n cadrul unui r$nd, valorile de pe coloane pot fi
accesate ^n orice ordine. Un obiect de tip ResultSet men]ine
un cursor care indic@ spre r$ndul curent de date. Metoda
next() pozi]ioneaz@ cursorul pe urm@torul r$nd. De
asemenea exist@ metode de forma getXXX() pentru a
recep]iona coloane de un anumit tip din linia curent@.
Clasele DatabaseMetaData }i ResultMetaData
returneaz@ metadatele bazei de date sau lui ResultSet, prin
apelul metodei getMetaData() asupra Connection, respectiv
asupra ResultSet.
DriverManager
59
Connection DatabaseMetaData
ResultMetaData
Statement
Resultset
%nregistrarea driverelor JDBC
Driverele JDBC trebuie sa fie ^nregistrate de
DriverManager ^nainte de a fi folosite. Driverele JDBC se
^nregistreaz@ automat singure, odat@ ce au fost
^nc@rcate.
Metoda [Link]() poate fi utilizat@ pentru a
^nc@rca clasa specificat@ ^n interpreterul Java, dac@
aceast@ clas@ nu a fost deja ^nc@rcat@. Numele clasei
trebuie s@ fie calificat cu ^ntreaga specificare a pachetului,
de exemplu:
“[Link]”
Exemplul reflect@ driverul pentru bazele de date Oracle.
Clasa driver se nume}te OracleDriver }i este localizat@ ^n
pachetul [Link].
%nregistrarea se face a}adar:
[Link]("[Link]");
Aceast@ metod@ poate arunca, dac@ ceva merge gre}it,
excep]ia ClassNotFoundException, care trebuie tratat@
corespunz@tor.
60
DriverManager este utilizat pentru a deschide o conexiune la
o baz@ de date. %n JDBC, bazele de date sunt identificate
folosind o conven]ie bazat@ pe URL, dup@ cum urmeaz@:
Jdbc:<protocol>:<connectString>
Protocolul reprezint@ tipul sursei de date, cum ar fi oracle
sau odbc. Fiecare driver JDBC se auto^nregistreaz@ pentru
a suporta un protocol particular. Protocolul este folosit apoi
de c@tre DriverManager pentru a identifica driverul care
suporta acest protocol particular.
ConnectString este ^n]eles de driver }i identific@ o surs@
de date particular@ }i informa]ia asociat@ de configurare:
Jdbc:oracle:thin:@<host>:<port>:<sid>
Vom exemplifica modul de conectare la o baz@ de date
Oracle utiliz$nd driverul Oracle JDBC Thin.
DriverManager are o metoda getConnection ce prime}te ca
parametru URL-ul asociat bazei de date la care dorim s@ ne
conect@m, }i returneaz@ un obiect de tip Connection ce
reprezint@ conexiunea:
Connection c;
c=[Link]( “jdbc:oracle:thin:my
host:1521:orc1” , “utilizator” , “parola” );
Metoda getConnection() arunc@ excep]ia SQLException
dac@ apar probleme, cum ar fi: nume de surs@ de date
nerecunoscut, nume de utilizator sau parola incorect@.
Ob]inerea metadatelor bazei de date
Clasa Connection are o metod@ getMetaData(), ce
returneaz@ un obiect de tip DatabaseMetaData. Acest obiect
ofer@ acces la o multitudine de informa]ii despre
metadatele ce sunt caracteristice bazei de date. Exist@
61
peste o sut@ de metode ^n aceast@ clas@ care acoper@
^n totalitate metadatele disponibile despre o baz@ de date.
De exemplu :
Connection c;
DatabaseMetaData md=getMetaData();
String s=[Link]();
String s2=[Link]();
Boolean b=[Link]();
Metoda getSQLKeywords() returneaz@ o list@ a cuvintelor
cheie, metoda supportTransactions() identific@ dac@
tranzac]iile pot fi aplicate bazei de date.
Cele mai multe metode ale clasei DatabaseMetaData
arunc@ excep]ia SQLException.
Construc]iile (Statements)
Sunt definite trei interfe]e ce ofer@ urm@toarele
capabilit@]i :
- executarea cererilor de reg@sire }i a altor opera]ii LMD
(limbaj de manipulare a datelor) }i LDD (limbaj de
descriere a datelor) ,
- execu]ia construc]iilor precompilate
(PreparedStatement) ,
- apelul procedurilor storate (CallableStatement) .
Clasa Connection are o metod@ numit@
createStatement() ce returneaz@ un obiect de tip
Statement. Obiectul Statement este folosit pentru a efectua
reg@siri SQL neparametrizate }i a ob]ine rezultate sub
forma unui obiect de tip ResultSet.
Metoda executeQuery() este utilizat@ pentru a executa o
comand@ SQL select. Aceast@ metod@ returneaz@ un
singur ResultSet ce poate fi parcurs r$nd cu r$nd. La un
moment dat, un singur ResultSet poate fi deschis printr-un
obiect Statement, cu alte cuvinte trebuie folosite mai multe
obiecte Statement pentru a dispune de mai multe ResultSet-
uri ^n acela}i timp.
62
Metoda executeUpdate() poate fi utilizat@ pentru a
efectua comenzile SQL: update, insert, delete sau comenzi
LDD.
De exemplu:
Statement st=[Link]();
ResultSet rs=[Link]( “select nume from
clienti” );
[Link]( “delete from clienti where
sal>5000000);
Metoda execute() efectueaz@ o comand@ SQL care ar
putea returna mai multe seturi de date. Seturi de date
multiple pot fi generate de o procedur@ storat@.
Un obiect ResultSet permite accesul la datele generate de
execu]ia unei reg@siri SQL. O anume coloan@ poate fi
referit@ fie prin num@rul ei de ordine fie prin numele ei.
%n momentul ^n care o valoare este returnat@
programului Java, driverul JDBC ^ncearc@ s@ converteasc@
data respectiv@ la un tip specific Java, astfel ^nc$t s@
rezulte o valoare Java potrivit@.
Un ResultSet este automat ^nchis de Statement-ul ce l-a
generat, c$nd Statement-ul este ^nchis, reexecutat sau este
folost pentru a returna urm@torul set de date dintr-o
secvent@ de rezultate multiple.
De exemplu:
ResultSet rs=[Link](“select nume from
clienti”);
While([Link]()) {
String nume=[Link](1);
}
Exist@ o metod@ oferit@ de ResultSet, numit@
findColumn(), ce prime}te ca parametru un string,
reprezent$nd numele coloanelor c@utate, }i returneaz@
num@rul de ordine al coloanei.
63
Clasa ResulSet are o metod@ numit@ getMetaData(), care
returneaz@ un obiect de tipul ResultSetMetaData. Un obiect
ResultSetMetaData poate fi folosit pentru a afla informa]ii
despre tipurile }i propriet@]ile coloanelor dintr-un ResultSet.
%n exemplul urm@tor vom determina num@rul coloanelor
din ResultSet, precum }i pentru fiecare coloan@ numele
coloanei }i titlul coloanei folosit la afi}@ri:
ResultSetMetaData md=[Link]();
While ([Link]()) {
For(int i=0;i< [Link]();i++) {
String lbl = [Link]();
String name=[Link]();
}
}
Maparea tipurilor bazei de date la tipurile Java se face de
c@tre ResultSet. Pentru fiecare tip de dat@ din baza de date
exist@ un tip corespunz@tor Java, precum }i o func]ie
getXXX() pentru a ob]ine valoarea unei coloane de un tip
particular. De exemplu pentru tipurile char }i varchar exist@
tipul Java [Link] }i metoda getString(); iar pentru
tipul date exist@ [Link] }i getDate().
Comenzi precompilate (Prepared Statements)
PreparedStatement este derivat@ din clasa Statement dar
este diferit@ sub doua aspecte esen]iale:
- dac@ un obiect Statement se execut@ de mai multe ori,
comanda sa SQL este compilat@ de fiecare dat@.
PreparedStatement este mult mai eficient, fiind compilat
doar o singur@ dat@, atunci c$nd este preg@tit
PreparedStatement-ul. A}adar dac@ se preconizeaz@ c@
o comand@ SQL va fi executat@ de mai multe ori, este
mai eficient s@ fie utilizat un PreparedStatement , dec$t
un Statement.
- alt@ diferen]@ important@ este c@ ^n stringul SQL al
unui PreparedStatement pot fi specifica]i parametrii.
64
Fiecare parametru este suplinit de un semn de ^ntrebare
^n comanda SQL. Atunci c$nd va fi executat
PreparedStatement-ul, vor fi furnizate valorile actuale ale
parametrilor.
Cu ajutorul metodelor setXXX() are loc setarea valorilor
parametrilor. De exemplu:
PreparedStatement ps=[Link](“update
salariati set sal=? “);
[Link](1,new BigDecimal(55000));
[Link]();
Proceduri storate
Obiecte de tipul CallableStatement sunt utilizate pentru a
executa procedurile storate SQL. JDBC ofera un mod de
implemetare a procedurilor storate astfel inc$t s@ poat@ fi
apelate ^n mod standard din orice SGBDR (sistem de
gestiune a bazelor de date rela]ional).
Sintaxa are dou@ forme, prima av$nd un parametru rezultat
iar cea de a doua nu:
{ ?=call [ .....]}
{ call [.....]}
Dac@ exist@ un parametru rezultat acesta va trebui
^nregistrat ca un parametru de tip OUT. Ceilal]i parametrii
pot fi folosi]i at$t ca intr@ri c$t }i ie}iri. Parametrii sunt
referi]i ^n mod secven]ial prin numere. Primul parametru
are indexul 1.
GetSal (nume in varchar2,slujba out varchar2)
return numeric;
// .... implementarea functiei
callableStatement cs=[Link]( “{?= call
getSal(?,?)}” );
[Link](1,[Link]);
[Link](2,”Stefan”);
[Link](3,[Link]);
[Link]();
65
[Link](“ Stefan produce” +
[Link](1) + “ca” + [Link](3));
Procedura din exemplul de mai sus are trei parametrii, din
care unul rezultat, unul de intrare (numele) }i altul de ie}ire
(slujba). Metoda prepareCall() apeleaz@ procedura storat@.
Metoda registerOutParameter() este folosit@ pentru a
^nregistra parametrii OUT.
Cu metodele setXXX() are loc setarea valorii parametrilor de
intrare (IN). Metoda executeUpdate() este apelat@ pentru a
executa procedura storat@. Metodele getXXX() sunt utilizate
pentru a primi valorile parametrilor OUT.
Tranzac]iile
%n JDBC tranzac]iile bazei de date sunt controlate de
obiectul Connection. C$nd este creat un obiect Connection
acesta are o proprietate autoCommit care define}te daca
tranzac]iile se finalizeaz@ automat dupa fiecare comand@
SQL executat@, sau dac@ ele se finalizeaz@ doar la cererea
explicit@ a programatorului.
A}adar tranzac]iile sunt guvernate de proprietatea
autoCommit din Connection. Ini]ial este setat@ true,
gener$nd o tranzac]ie nou@ pentru fiecare comand@ SQL.
Pentru a o modifica se apeleaz@ metoda setAutoCommit().
Prezent@m ^n continuare detaliat comportamentul lui
autoCommit.
Dac@ o conexiune este ^n modul auto-commit, toate
comenzile SQL vor fi executate }i tratate ca tranzac]ii
individuale. Finalizarea are loc atunci c$nd comanda se
^ncheie ori c$nd o nou@ execu]ie are loc. Dac@ o comand@
returneaz@ un ResultSet, comanda se ^ncheie atunci c$nd
ultimul r$nd din ResultSet a fost primit sau c$nd ResultSet-ul
a fost ^nchis.
Daca autocommit() este setat pe false, atunci comenzile
SQL sunt grupare ^n tranzac]ii, care sunt finalizate fie prin
commit(), fie prin rollback(). Metoda commit() face
permanente schimb@rile ap@rute de la ultimul rollback()
66
sau commit() }i elibereaz@ toate lock-urile pe baza de date
de]inute de Connection. Metoda rollback() anuleaz@ toate
modific@rile efectuate de la ultimul commit() sau
rollback() }i de asemenea elibereaz@ toate lock-urile pe
baza de date de]inute de Connection.
Extensii Oracle la JDBC
Driverele Oracle JDBC ofer@ o serie de extensii pentru a
spori eficien]a, fiabilitatea }i flexibilitatea. Aceste extensii
sunt localizate ^n subclasele specifice Oracle din clasele }i
interfe]ele JDBC standard.
Aceste extensii permit:
- prefetch (setarea num@rului de r$nduri care vor fi primite
de la baza de date);
- specificarea prealabil@ a tipului coloanei ^n comand@,
economisindu-se astfel timp;
- pseudocoloana ROWID.
67