Sunteți pe pagina 1din 9

sql & java

Programare SQLJ
Cnd dorim s scriem un program care acceseaz o baz de date dintr-un program Java, ntrebarea este: Ce vom
folosi, JDBC sau SQLJ?
Monica Cioat

n ultimii ani, Java a devenit limbajul preferat al dezvoltatorilor de aplicaii Internet/Intranet. Apleturile i
servleturile Java apar peste tot pe Web, oferind o bogat funcionalitate unui mediu, pn nu demult, eminamente
static. Siturile Web au devenit veritabile furnizoare i colectoare de informaii, iar de cele mai multe ori aceste
informaii trec printr-o baz de date. Deci accesarea unei baze de date este o problem care trebuie rezolvat de
fiecare dat cnd dorim s construim astfel de aplicaii. Dac ai mai accesat din Java baze de date, vei spune JDBC
este soluia i nu v pot contrazice; dar pot spune c soluia JDBC poate fi acum ameliorat graie noii tehnologii
SQLJ menit s simplifice acolo unde e posibil modul de acces la baza de date.
Un exemplu
SQLJ este o cale standard de includere a comenzilor SQL statice direct n programe Java. Noua tehnologie este
rezultatul unei colaborri Oracle, IBM, Sybase, Tandem i Informix, dup modelul standardului ANSI/ISO de legare
a comenzilor SQL n programe C, COBOL, FORTRAN sau alte limbaje. Oracle implementase anterior Pro*C un
produs ce permite utilizarea comenzilor SQL n programe C. Cu alte cuvinte, SQLJ ar fi un soi de Pro*Java.
Deoarece Pro*anything desemneaz o tehnologie (specific)/ proprietar Oracle, iar SQLJ este un standard deschis,
nu s-a ales denumirea de Pro*Java.
Dup cum vom vedea n acest articol, dei SQLJ simplific scrierea, gestiunea i depanarea aplicaiilor Java, el nu
nlocuiete JDBC. Din contr, implementarea Oracle pentru SQLJ chiar folosete JDBC. Pentru a nelege cum
funcioneaz SQLJ s lum urmtorul exemplu: presupunem c avem o tabel, emp, n baza de date care conine
informaii despre angajaii unei firme, iar printre cmpurile acesteia: ename - numele i sal - salariul angajatului.
Dorim s afim numele tuturor angajailor care au salariul mai mare dect o valoare dat. Codul JDBC n acest caz
ar putea fi:
// (Presupunem c avem deja un obiect JDBC Connection conn)
// definim variabile Java
String name;
int id=37115;
float salary=20000;
// Construim un obiect JDBC PreparedStatement.
PreparedStatement pstmt = conn.prepareStatement
("select ename from emp where empno=? and sal>?");
pstmt.setInt(1, id);
pstmt.setFloat(2, salary);
// Executam o interogare; valorile obtinute sunt //asignate unei variabile Java.
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
name=rs.getString(1);
System.out.println("Numele este: " + name);
}
rs.close()
pstmt.close();
Primele trei linii definesc variabilele Java name, id i salary. Urmtoarea linie realizeaz un apel de pregtire a unei
instruciuni (prepared statement). Presupunnd c deja este stabilit o conexiune prin JDBC la baza de date, putem
folosi metoda prepareStatement() - metod a obiectului conn derivat din clasa Connection (conexiune). O
PreparedStatement este folosit atunci cnd n interiorul comenzii SQL avem nevoie de un set dinamic de valori,
ceea ce nseamn c o aceeai instruciune gata pregtit (prepared statement) poate fi folosit de oricte ori pentru
diferite valori ale variabilelor. Interogarea este format ntr-o instan (pstmt) a clasei PreparedStatement n timp
valorile variabile sunt pstrate n variabile Java i transmise prin apelurile pstmt.setInt() i pstmt.setFloat().
Primul "?"este nlocuit cu valoare variabilei id, deci un int a crui valoare este 37115. Al doilea "?" este nlocuit cu
valoarea variabilei salary (un float a crui valoare este 20000). Urmeaz execuia interogrii, datele rezultat fiind
returnate ntr-un obiect ResultSet al JDBC. n final, datele sunt extrase din setul rezultat i afiate. Un set rezultat
conine de obicei mai multe linii de date, dar n acest exemplu avem o singur linie. Dup cum se poate lesne
constata, avem destul de lucru pentru a realiza o simpl interogare.
Ne putem imagina atunci complexitatea apelurilor SQL multilinie n care avem mai multe variabile Java. Inevitabil
dup un timp ne vom pune ntrebarea "Cum putem face ca din Java interogrile s fie mai uor de realizat?"
Simplificarea procesului de interogare
Aa cum bnuii deja rspunsul la ntrebarea noastr este: folosind SQLJ. Pentru a ne da seama de avantajele SQLJ
s transcriem exemplul de mai sus folosind SQLJ:
String name;
int id=37115;
float salary=20000;
#sql {select ename into :name from emp where empno=:id and sal>:salary);
System.out.println("Name is: " + name);
Ce observm analiznd acest cod? Primul lucru, un cod mult mai concis! Explicaia? SQLJ permite comenzilor SQL
s fie legate direct n codul Java i accept ca variabilele Java s fie folosite direct n comenzile SQL.
Mai observm c fiecare variabila Java este precedat de dou puncte ":" i c prin intermediul acestor variabile
Java putem extrage cu uurin rezultatul interogrii, dac ... numai dac rezultatul execuiei comenzii SQL este o
reprezentat de o singur linie (o nregistrare)!
Componentele SQLJ
n continuare, n paralel cu explicarea pailor pe care-i face/ascunde SQLJ vom ncerca s identificm componentele
interne ale SQLJ i s vedem rolul pe care-l joac fiecare.
Translatorul SQLJ
Aceast component nu este altceva dect un preprocesor sau un precompilator scris complet n Java, i care este
rulat dup ce a fost creat sursa SQLJ (un fisier .sqlj). Translatorul accept comenzi SQL n interiorul comenzilor
executabile SQLJ. Comenzile executabile SQLJ, ca i declaraiile SQLJ, sunt precedate de #sql i pot fi combinate
cu cod Java n fiierul surs sqlj. ntr-o prim etap translatorul face o analiz sintactic i semantic a comenzilor
SQL legate, eventualele erori putnd fi depistate nc din aceast faz. Aceasta verificare se poate face conectat sau
nu la baza de date (on-line sau off-line), n funcie de opiunile setate. Dac se face o verificare on-line, SQLJ se
conecteaz la baza de date i verific existena tabelelor, a procedurilor stocate i compatibilitatea tipurilor
coloanelor referite cu a variabilelor gazd din Java. Nu acelai lucru se ntmpl cu codul JDBC care, fiind pur Java,
este compilat direct. Compilatorul nu tie nimic despre SQL, prin urmare erorile SQL nu vor putea fi depistate dect
la execuie.
n faza urmtoare translatorul proceseaz codul SQLJ, convertete operaiile SQL n apeluri SQLJ runtime,
genereaz codul de ieire i unul sau mai multe profile SQLJ. Este generat un profil separat pentru fiecare
conexiune, acolo unde se fac conexiuni (de exemplu, la baze de date diferite, sau n scheme diferite) i fiecare
comand SQL va avea mapat o intrare n profilul generat pentru conexiunea pe care o folosete. Translatorul
genereaz un fiier .java care conine:
- clasele definite i codul Java din fiierul surs .sqlj;
- definiiile claselor create ca rezultat al declaraiilor SQLJ;
- o definiie de clas pentru o clas specializat (numit i clas profile-key), clase pe care SQLJ le creaz i le
folosete n conjuncie cu profilele generate;
n final este apelat un compilator Java, care poate fi de exemplu JDK-ul de la Sun i se genereaz cte un fiier
.class pentru fiecare clas definit, cte un .class pentru fiecare declaraie SQLJ i cte un .class pentru fiecare clas
de tip profil cheie.
Profilele generate conin informaii despre toate comenzile SQL coninute n codul surs, tipurile de date care se
folosesc i tabelele accesate. Cnd aplicaia ruleaz sunt accesate profilele pentru a se obine operaiile SQL i a le
transmite driverului JDBC. Implicit profilele sunt puse n fiiere de resurse serializate .ser, dar se pot converti n
fiiere .class tot n procesul de translaie. (Observaie: unele browsere, de ex. Netscape Navigator 4.x nu suport
fiiere de resurse cu extensia .ser. Pentru a rezolva aceast problem se utilizeaz o opiune de conversie -ser2class
care are ca efect conversia resurselor n fiiere .class).
SQLJ runtime
Componenta runtime este apelat de fiecare dat cnd este rulat o aplicaie SQLJ. La rndul ei scris complet n
Java, aceast component implementeaz aciunile descrise de operaiile SQL i acceseaz baza de date folosind un
driver JDBC. Standardul SQLJ nu specific ca la rulare s se foloseasc un driver JDBC pentru accesarea bazei de
date, ns Oracle SQLJ are nevoie de un astfel de driver. Chiar mai mult, este necesar un driver Oracle dac aplicaie
folosete construcii proprii Oracle.
Elemente de limbaj SQLJ
Comenzile SQLJ ncep cu construcia #sql i se mpart n dou categorii:
(1) declaraii de iteratori; iteratorii sunt similari cu seturile rezultat din JDBC i folosii pentru extragerea
rezultatelor interogrilor multi-linie sau declaraii de conexiuni n diferite scheme ale mai multor baze de date;
(2) comenzi executabile, folosite pentru executarea operaiilor SQL;
Declaraiile SQLJ pot s apar n fiierul surs n afara claselor, n clase, n clase ncuibrite, ns nu i n interiorul
metodelor. De exemplu:
Declaratie SQLJ; //legal
Class Outer
{
Declaratie SQLJ; //legal
Class Inner
{
Declaratie SQLJ; //legal
}
void func()
{
Declaratie SQLJ; // Ilegal
}
}
Conexiuni
Toate drivere Oracle ntrein clasa oracle.jdbc.driver.OracleDriver.
Pentru conectarea la baza de date se poate folosi unul sau mai multe drivere. Pentru stabilrea conexiunii la baza de
date se poate folosi metoda connect() a clasei oracle.sqlj.runtime.Oracle, pentru care vor trebui specificate: driverul
folosit, url-ul bazei de date, numele i parola utilizatorului, pentru identificarea schemei n care se va face
conexiunea. Iat un exemplu:
Oracle.connect("jdbc:oracle:thin:@localhost:1521:orcl", "scott", "tiger");
Parametrii conexiunii pot fi specificai ntr-un fiier de proprietai, connect.properties, care va fi referit la apelul
metodei:
Oracle.connect( MyClass.class, "connect.properties");
// MyClass este numele clasei.
Dac vrem s facem mai multe conexiuni la baze de date diferite sau n scheme diferite ale aceleiai baze de date,
vom construi pentru fiecare conexiune cte o instan a clasei DefaultContext:
DefaultContext ctx1 = Oracle.getConnection ("jdbc:oracle:thin:@localhost1:1521:orcl1", "scott", "tiger");
DefaultContext ctx2 = Oracle.getConnection ("jdbc:oracle:thin:@localhost2:1521:orcl2", "bill", "lion");
Sunt dou modaliti de a schimba ntre cele dou conexiuni:
- prin specificarea explicita a conexiunii folosite:
#sql [ctx1] { SQL operation };
...
#sql [ctx2] { SQL operation };
sau:
- prin setarea unei conexiuni ca fiind conexiunea implicit folosind metoda static setDefaultContext() a clasei
DefaultContext:
DefaultContext.setDefaultContext(ctx1);
...
#sql { SQL operation }; // comenzi pentru ctx1
#sql { SQL operation };
#sql { SQL operation };
...
DefaultContext.setDefaultContext(ctx2);
...
#sql { SQL operation }; // comenzi pentru ctx2
#sql { SQL operation };
#sql { SQL operation };
...
Se pot utiliza conexiuni diferite la translaie i runtime i asta fiindc aplicaia final va folosi un alt mediu. n acest
caz se pot folosi opiuni n linia de comand n care se lanseaz n execuie translatorul (folosind comanda sqlj), prin
care s se specifice URL-ul i tipul driverului folosit (dup opiunea -url), utilizatorul (-user) i parola (-password).
Iteratori
Iteratorii sunt folosii pentru recepionarea datelor unei interogri. Iteratorul trebuie construit n aa fel nct s fie
compatibil cu datele care va trebui s le recepioneze, att n ceea ce privete tipul ct i numrul cmpurilor lor.
Sintaxa folosit va fi:
#SQL <modificatori>iterator nume_clasa_iterator{tip declaratii};
Modificatorii sunt opionali i pot fi orice modificatori Java standard ca: public, static, etc. Sunt dou tipuri de
iteratori: iteratori cu nume i poziionali.
n cazul interatorilor cu nume trebuie specificate numele (identic cu al coloanei de unde se iau datele) i tipul, care
de asemenea trebuie s fie compatibil cu al coloanei respective.
#sql public iterator EmpIter(String ename, double sal);
Pentru iteratorii poziionali se va specifica numai tipul coloanelor:
#sql public iterator EmpIter(String, double);
n ambele cazuri translatorul creeaz clasa public EmpIter, cu dou atribute.
n declaraia oricrei clase iterator se pot specifica una sau mai multe interfee care vor fi implementate de clasa
generat, folosind urmtoarea sintax:
#sql <modificatori> iterator nume_clasa_iterator implements interfata1, interfata2, interfataN (declaratii de tip);
i se pot specifica i iniializa una sau mai multe constante care s fie incluse n definiia clasei generate. Constantele
sunt public static final. Se folosete urmtoarea sintax:
#sql <modificatori> iterator nume_clasa_iterator with (var1=valoare1,,varN=valoareN)
(declaratii de tip);
Observaie: Clauzele with i implements sunt plasate ntotdeauna naintea declaraiilor de tip. Acolo unde apare i
clauza with si implements, clauza implements va fi plasata prima. Lista de dup with va fi prins ntre paranteze.
Exemplu:
#sql public iterator MyIter with
(TYPECODE=OracleTypes.NUMBER)
(String empname, int empnum);
Clasa declarat MyIter, va avea un atribut TYPECODE care va fi public static final de tip int i iniializat cu
valoarea typecode pentru tipul de date NUMBER aa cum este definit n clasa Oracle JDBC
oracle.jdbc.driver.OracleTypes.
Exemplul urmtor conine att clauzele implements ct i with.
#sql public iterator MyIter implements
mypackage.MyIterIntfc
with (holdability=true)
(String empname, int empnum);
Comenzi SQLJ executabile
O comand SQLJ executabil este o comand SQL suportat de driverul JDBC folosit (comenzi DML, DDL, sau de
control al tranzaciilor) precedat de clauza SQLJ #sql.
Comenzile executabile trebuie s respecte urmtoarele reguli:
- pot s apar n codul Java oriunde este permis un bloc (deci n interiorul definiiei metodelor i a blocurilor statice
de iniializare);
- trebuie prinse ntre acolade: {...}.
- tot ce este cuprins ntre acolade se consider o comand executabil SQLJ i trebuie s respecte regulile de sintax
SQL, cu excepia expresiilor gazd Java.
Iat un exemplu:
#sql { SQL operation };
// pentru comenzi care nu au iesire, de exemplu INSERT
#sql result = { SQL operation };
// pentru comenzi care au iesire, de exemplu SELECT
Observaie: Numai operaiile DML (SELECT, UPDATE, INSERT i DELETE) pot fi analizate i verificate sintactic
de translatorul SQLJ, translator folosind conexiunea la baza de date. Comenzile DDL (cum sunt CREATE ..., sau
ALTER...), comenzile de control al tranzactiilor (COMMIT sau ROLLBACK), sau orice alt comand SQL nu va
putea fi verificat.
Presupunem c tabela EMP a fost creat cu comanda:
CREATE TABLE EMP (EMPNAME CHAR(30),SALARY NUMBER );
O comand de inserare n tabel poate fi:
#sql { INSERT INTO emp (empname, salary) VALUES ('Joe', 43000)};
Dac dorim ca operaia de inserare s fie fcut ntr-o schem spre care avem stabilit o alt conexiune, diferit de
cea implicit, cu numele ctx, comanda va fi:
#sql [ctx] { INSERT INTO emp (empname, salary) VALUES ('Joe', 43000) };
n cadrul comenzilor executabile pot s apar i blocuri PL/SQL, ntre acolade, exact ca i orice alt comand SQL.
#sql {
DECLARE
n NUMBER;
BEGIN
n := 1;
WHILE n <= 100 LOOP
INSERT INTO emp (empno) VALUES(2000 + n);
n := n + 1;
END LOOP;
END;
};
n interiorul ciclului se insereaz nregistrrii noi care vor avea n cmpul empno valori de la 2001 la 2100.
n continuare vom lua un alt exemplu de comand, care folosete expresii gazd Java precedate de (:). Expresiile
gazd sunt folosite pentru transmiterea valorilor ntre codul Java i comenzile SQL. Acestea pot fi n cazul cel mai
simplu o simpl variabil Java, dar pot s fie i apeluri de metode care returneaz valori, valori ale cmpurilor clasei,
tablouri de elemente, expresii condiionale de forma (a ? b :c), expresii logice, sau expresii la nivel de bit (bitwise
expressions). Ele pot fi de intrare (IN), de ieire (OUT) sau de intrare-ieire (INOUT).
#sql { UPDATE emp SET sal = :salary WHERE ename = :name };
Observaie: n ambele cazuri expresiile Java sunt de tip IN, dar se poate specifica i explicit:
#sql { UPDATE emp SET sal = :IN salary WHERE ename = :IN name };
Cnd o interogare returneaz o singur linie, SQLJ permite specificarea unor variabile gazd Java, n interiorul
comenzii SQL, pentru extragerea rezultatului interogrii:
#sql { SELECT expression1,..., expressionN
INTO : host_exp1,..., :host_expN
FROM datasource <optional clauses> };
Dac comanda SELECT INTO ntoarce mai mult de un rezultat se va genera eroare:
String empname;
...
#sql { SELECT ename INTO :empname FROM emp WHERE enum = 28959 };
Observaie: OUT este implicit pentru o lista INTO, dar se poate specifica i explicit:
#sql { SELECT ename INTO :OUT empname
FROM emp WHERE enum = 28959};
De cele mai multe ori interogrile pe o tabel a bazei de date ntorc mai multe linii ale tabelului. n acest caz datele
vor fi recepionate n iteratori, care trebuie mai nti pregtii special, specificndu-li-se coloanele i tipul acestora,
spre deosebire de obiectul ResultSet din JDBC, care este instaniat generic. n JDBC clasa java.sql.ResultSet poate
conine, n principiu, orice numr de coloane de orice tip.
#sql iterator SalNamedIter (String empname, float newsalary)
...
class MyClass {
void func() throws SQLException {
...
SalNamedIter niter = null;
#sql niter = { SELECT name AS empname, oldsal + raise AS newsalary FROM empsal };
...
}
}
Putem declara i un iterator pozitional:
#sql iterator SalPosIter (String, float);
...
class MyClass {
void func() throws SQLException {
...
SalPosIter piter = null;
#sql piter = { SELECT name, oldsal + raise FROM empsal };
...
}
}
Iat i cteva reguli:
- Nu este indicat utilizarea unei sintaxe SELECT * pentru popularea unui iterator.
- n cazul iteratorului poziional numrul de coloane, tipul i ordinea lor din iterator trebuie s coincid cu numrul
coloanelor tabelului.
- n cazul iteratorilor cu nume este posibil ca numrul coloanelor iteratorului s fie mai mic dect al coloanelor
tabelului; n acest caz, dac la translatare nu se folosete opiunea -warn=nostrict se va da o atenionare.
- Iteratorii poziionali i cei cu nume sunt diferii i incompatibili cu orice tip de clase Java.
- Un iterator de un anumit tip nu poate fi convertit ntr-un iterator de alt tip;
- Coninutul unui iterator, similar cu un set rezultat, este determinat numai de starea bazei de date n momentul
execuiei SELECT-ului care-l populeaz;
- Actualizrile, inserarile, tergerile, commit-urile sau rollback-urile nu afecteaz iteratorii sau coninutul lor.
Iteratori cu nume vs. iteratori poziionali
n general pentru utilizarea unui iterator se parcurg urmtorii pai:
(1) Folosirea unei declaraii SQLJ pentru definirea clasei iteratorului (tipului iteratorului);
(2) Declararea unei variabile de tipul respectiv;
(3) Popularea variabilei iterator cu rezultatul unei interogri cu SELECT ;
(4) Accesarea coloanelor interogrii iteratorului. (modalitatea de acces depinde de tipul iteratorului, pozitional sau
numit).
(5) Cnd procesarea rezultatelor s-a terminat, iteratorul este nchis.
Iteratorii cu nume confer o mai mare flexibilitate prelucrrii deoarece selecia datelor se face prin numele
cmpurilor iteratorului. Concret, pentru fiecare cmp este construit automat o metod de acces la valoarea acestuia,
nemaifiind necesar respectarea locului/ordinii cmpurilor tabelului.
La definirea iteratorilor cu nume trebuie respectate urmatoarele reguli:
- Numele coloanelor s nu fie cuvinte rezervate Java;
- Coloanele s nu aib acelai nume cu metodele folosite de clasele iterator- next(), close(),getResultSet(),
isClosed().
- Numele coloanelor s nu fie identice cu numele tabelelor i s fie diferite ntre ele;
n cazul iteratorilor poziionali selecia datelor se face prin poziia cmpului, ceea ce face ca n acest caz accesul la
datele elementare s se realizeze mai greoi. Datele trebuiesc ncrcate direct n expresii gazd Java cu comanda
FETCH INTO i expresiile gazd trebuie s fie n ordinea corect.
Presupunem c dorim s populm un iterator cu date din tabelul:
CREATE TABLE PROJECTS (ID NUMBER(4), NAME VARCHAR(30),
START_DATE DATE,DURATION NUMBER(3) );
Declararea unei variabile iterator si popularea ei se face prin:
#sql public iterator ProjIter (String projname, int id, Date deadline);
ProjIter projs;
#sql projs =
{SELECT start_date + duration AS deadline, name, id
FROM projects WHERE start_date + duration >= sysdate};
Metoda next() este comun pentru toi iteratorii i joac acelai rol ca i metoda next() pentru un obiect ResultSet
din JDBC, ntorcnd true dac exist linia i mutindu-se la urmtoarea linie de date din setul rezultat. Datele din
fiecare linie sunt accesate apelind metoda iterator ce are acelasi nume cu al coloanei.
#sql projs = {
SELECT start_date + duration as deadline, projname, id
FROM projects
WHERE start_date + duration >= sysdate };
// Procesarea rezultatelor
while (projs.next()) {
System.out.println("Project name is " + projs.projname());
System.out.println("Project ID is " + projs.id());
System.out.println("Project deadline is " + projs.deadline());
}
// nchide iteratorul
projs.close();
...
n cazul iteratorilor poziionali se va specifica numai tipul coloanelor; pentru obinerea datelor din iteratorii
poziionali se va folosi comanda FETCH..INTO urmat de un apel al metodei endFetch() care va determina dac s-a
ajuns la sfritul datelor.
De exemplu:
#sql public iterator EmpIter (int, String, float);
// Declarm i iniializm variabile gazd
int id=0;
String name=null;
float salary=0.0f;
EmpIter emps;
#sql emps = { SELECT empnum, empname, empsal
FROM employees};
while (true) {
#sql { FETCH :emps INTO :id, :name, :salary };
if (emps.endFetch()) break;
// Acest test trebuie facut dup fetch,
// dar nainte de procesarea rezultatelor.
System.out.println("Name is " + name);
System.out.println("Employee number is " + id);
System.out.println("Salary is " + salary);
...
}
emps.close();
...
Observaii:
- Nu este necesar utilizarea explicita a metodei next() pentru pozitionarea iteratorului, deoarece apelurile FETCH
mut implicit iteratorul pe urmtoarea linie/nregistrare.
- Iniial metoda endFetch() returneaz true nainte ca liniile s fie ncrcate, iar dup ce o linie a fost ncrcat cu
succes va returna false pn cnd i ultima linie va fi ncarcat, dup care va returna din nou true.
- Metoda endFetch() trebuie apelat nainte ca setul rezultat s fie procesat, deoarece FETCH nu depisteaz
excepiile SQL cnd ajunge la sfritul datelor;
Comenzi de asignare (SET)
SQLJ ne permite s atribuim o valoare unei expresii gazd Java n interiorul comenzilor SQL. Comanda de asignare
va avea urmtoarea sintax:
#sql { SET : host_exp = expression };
host_exp este expresia gazd, de exemplu o variabil sau un tablou de indeci. expression poate fi un numr, o
expresie gazd sau o expresie aritmetic, un apel de funcie. Implicit expresiile gazd sunt de ieire OUT dar acest
lucru se poate specifica explicit:
#sql { SET :OUT host_exp = expression };
Dac folosim IN sau INOUT se va genera eroare n faza de translatare.
Comanda anterioar este funcional echivalent cu:
#sql { BEGIN :OUT host_exp := expression; END; };
Un exemplu de asignare:
#sql { SET :x = foo1() + foo2() };
Valoarea atribuit lui x este suma valorilor returnate de funciile foo1() i foo2() - se presupune c tipul lui x e
compatibil cu tipul celor dou funcii.
Apelul procedurilor i funciilor stocate
Funciile i procedurile stocate pot fi scrise n Java, PL/SQL (n Oracle), sau orice alt limbaj de programare acceptat
de baza de date utilizat. Pentru apelul procedurilor se va folosi comanda CALL:
#sql { CALL PROC(< PARAM_LIST>) };
PROC este numele procedurii stocate, opional poate s aib parametrii care pot s fie de intrare, ieire sau intrare-
ieire.
Presupunem c avem definit procedura PL/SQL:
CREATE OR REPLACE PROCEDURE MAX_DEADLINE (deadline OUT DATE) IS
BEGIN
SELECT MAX(start_date + duration) INTO deadline FROM projects;
END;
n SQLJ apelm procedura MAX_DEADLINE astfel:
java.sql.Date maxDeadline;
...
#sql { CALL MAX_DEADLINE(:out maxDeadline) };
Funciile stocate returneaz o valoare i eventual au o list de parametrii care pot fi de intrare, ieire sau intrare-
ieire. Apelul funciilor se face prin utilizarea comenzii VALUES. n SQLJ standard, funciile trebuie s fie prinse
ntre paranteze rotunde. n Oracle acest lucru este opional.
#sql result = { VALUES( FUNC(< PARAM_LIST>)) };
Observaie: Dac dorim ca aplicaia s fie compatibil cu Oracle7, la funcii nu se vor pune paranteze dac lista
parametrilor este vid.
#sql { CALL MAX_DEADLINE }; //da
#sql { CALL MAX_DEADLINE() }; //nu
Controlul tranzaciilor
Att n SQLJ ct i n JDBC se poate lucra tranzacional. Controlul tranzaciilor se poate face automat sau manual.
Se poate specifica utilizarea unui commit automat prin setarea unui flag auto-commit n momentul definirii
conexiunii SQLJ, specificnd true pentru ultimul parametru al metodelor: Oracle.connect() sau
Oracle.getConnection():
Oracle.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "scott", "tiger", true);
sau folosind setAutoCommit() obiectului JDBC Connection, dac conexiunea a fost deja creat;
ctx.getConnection().setAutoCommit(false);
ctx.getConnection().setAutoCommit(true);
Se poate realiza un control manual al comenzilor tranzaciei folosind comenzile SQLJ commit i rollback:
#sql { commit };
#sql { rollback };
Evident c dac se seteaz un commit automat comenzile explicite commit sau rollback nu-i mai au rostul.
Commit-urile, fie ele automate sau manuale i rollback-urile nu afecteaz seturile rezultat deschise i iteratorii. S
considerm de exemplu cazul n care dorim s facem un SELECT, apoi un UPDATE, iar apoi un COMMIT. Un set
rezultat sau un iterator populat de comanda SELECT nu va fi afectat de UPDATE i nici de COMMIT.
Dac facem mai nti un UPDATE, apoi SELECT i ROLLBACK, setul rezultat sau iteratorul populat de SELECT
va conine datele actualizate, fr a lua n considerare ROLLBACK-ul.
Unde folosim SQLJ
Dup ce am vzut ce este, cum putem s folosim SQLJ, rmne s stabilim unde poate fi utilizat sau altfel spus cnd
vom apela la SQLJ. Deoarece implementarea Oracle a SQLJ folosete JDBC pentru a comunica cu baza de date,
programatorii SQLJ se pot bucura de aceeai flexibilitate ca i programatorii JDBC n dezvoltarea aplicaiilor.
Oracle ofer trei tipuri de drivere JDBC: un driver subire (thin) 100% pur Java, un driver OCI (Oracle Call
Interface) i un server-side driver ( "KPRB" Kernel PRogram Bundled).
Putem folosi oricare din aceste drivere, la fel de bine cum se pot folosi i altele construite de ali productori. De
exemplu, se poate folosi SQLJ ntr-un applet care ruleaz ntr-un browser, i care folosete un thin driver (de numai
300K). Ca alternativ putem folosi SQLJ ntr-un client sau ntr-un nivel din mijloc al aplicaiei folosind driver OCI
driver (care poate oferi mai multe avantaje comparativ cu un thin driver, dar i dezavantajul faptului c are o parte
ce trebuie n prealabil instalat pe client) sau se poate folosi SQLJ pe partea server, n proceduri i funcii stocate
Java, triggere, Enterprise JavaBeans sau obiecte CORBA. Accesul se face prin Oracle JDBC site-server. Oracle8i
Server include un translator SQLJ, ceea ce nseamn c fiierele surs de pe partea server pot fi translatate direct pe
server.
n crearea unei aplicaii pentru Oracle8i Server sunt mici diferene ntre codul care va fi folosit pe partea server i cel
de pe client:
- putem avea o singur conexiune, care trebuie s ofere acces la baza de date n care codul ruleaz;
- conexiunea este implicit, aadar nu trebuie specificat explicit,
- spre deosebire de conexiunile client, conexiunea de pe server nu poate fi nchis.
Codul se poate translata i compila att pe partea client cit si pe server. Dac acest lucru este realizat pe client, se pot
ncrca fiierele .class i de resurse de pe server pe propria main, sau folosind translator de pe server.
ntrebarea care se pune n acest moment ar putea fi: "Dac SQLJ este mai avantajos dect JDBC, de ce s mai
folosim JDBC?" n momentul de fa SQLJ i JDBC sunt API-uri complementare. SQLJ poate fi folosit numai
pentru SQL-uri statice, deci cnd comanda SQL este cunoscut n momentul dezvoltrii. De regul marea majoritate
a comenzilor SQL sunt statice, iar pentru comenzile SQL dinamice, construite n timpul rulrii, se va folosi JDBC.
Din acest punct de vedere nu exist probleme fiindc dac este necesar n interiorul unei aplicaii SQLJ se pot folosi
i construcii JDBC.
Concluzii
Programatorii Java au la dispoziie pentru accesarea bazelor de date dou alternative: JDBC sau SQLJ. Singur JDBC
poate fi suficient dar are dou slbiciuni:
- codul este lung i greoi, interogrile realizndu-se destul de dificil;
- erorile din interiorul operaiilor SQL nu pot fi depistate dect la rulare.
SQLJ rezolv ambele probleme:
- permite comenzilor SQL cu variabile Java s apar direct n comenzi SQLJ, simplificnd codul i fcndu-l mai
clar.
- translatorul SQLJ verific sintaxa i semantica comenzilor SQL, permind depistarea erorilor nainte de rulare.
n plus, SQLJ asigur aceleai verificri la rulare ca i JDBC.
n general JDBC i SQLJ vor fi vzute ca tehnologii complementare deoarece:
- SQLJ foloseste JDBC pentru a comunica cu baza de date n timpul translatrii (dac folosim verificrile online) i
rularii.
- JDBC este necesar pentru comenzile SQL dinamice, unde operaiile SQL sunt determinate pe parcursul rulrii
aplicaiei;
- Se poate mixa codul aceluiai program ntre cod JDBC i cod SQLJ.
Ambele tehnologii pot fi folosite la orice nivel:
- ntr-un aplet ce foloseste, de exemplu, un driver thin JDBC,
- ntr-o aplicaie pe partea client sau
- la nivelul din mijloc, folosind un driver thin sau un driver Oracle OCI;
- ntr-o procedura stocat, obiecte CORBA sau JavaBeans care ruleaz n Oracle8i i care folosesc Oracle server-
side JDBC driver.

S-ar putea să vă placă și