SQLJ este un subset al standardului SQL , care vizează combinarea avantajelor sintaxei SQL și Java pentru comoditatea implementării logicii de afaceri și a lucrului cu date. Acest standard a fost dezvoltat de un consorțiu format din IBM , Micro Focus , Microsoft , Compaq (mai precis, divizia sa DBMS, care poate fi atribuită mai degrabă companiei achiziționate Tandem ), Informix , Oracle , Sun și Sybase .
La momentul apariției consorțiului JSQL (care mai târziu a devenit același nume cu standardul pe care l-a dezvoltat) în 1997, ideea interacțiunii dintre DBMS relațional și programele Java nu era nouă. JavaSoft ( o subsidiară a Sun) a dezvoltat deja interfața JDBC ( Java DataBase Connectivity ) inclusă în standardul de limbaj de la lansarea JDK 1.1. Cu toate acestea, din anumite motive (vezi SQLJ și JDBC ), caracteristicile oferite de această interfață nu au fost suficiente.
Specificația standard SQLJ constă din trei părți:
Până la sfârșitul anului 1998, toate cele trei niveluri ale specificației au fost finalizate și transmise ANSI pentru a fi luate în considerare ca completări la standardul SQL. Primele două părți ale noului standard au fost incluse, respectiv, în părțile SQL/OLB și SQL/PSM ale standardului SQL:1999 ; a treia parte a fost inclusă ca un modul separat SQL/JRT în standardul SQL:2003
De obicei, în legătură cu dezvoltarea aplicațiilor care funcționează cu baza de date, SQLJ este de obicei înțeles ca nivelul 0.
Iată un exemplu simplu de clasă Java care utilizează SQLJ pentru a obține rezultate de interogare de la Oracle .
import java.sql.* ; import oracle.sqlj.runtime.Oracle ; public class SingleRowQuery extinde Base { public static void main ( String [] args ) { try { connect (); singleRowQuery ( 1 ); } catch ( SQLException e ) { e . printStackTrace (); } } public static void singleRowQuery ( int id ) aruncă SQLException { String fullname = null ; String street = null ; # sql { SELECT numele complet , strada INTO : OUT nume complet , : OUT strada FROM client WHERE ID = : IN id }; Sistem . afară . println ( "Client cu ID = " + id ); Sistem . afară . println (); Sistem . afară . println ( nume complet + " " + strada ); } }Din codul de mai sus, este clar că o singleRowQueryinterogare SQL este încorporată în textul procedurii în sine, iar această încorporare este organizată în conformitate cu anumite reguli:
Toate construcțiile sintactice vor fi discutate în detaliu mai jos.
Este logic că se pune întrebarea cu privire la motivele creării a două standarde paralele pentru implementarea tehnologiilor de acces DBMS.
Pentru început, este de remarcat faptul că SQLJ și JDBC aparțin unor familii diferite de standarde și sunt diferite din punct de vedere conceptual. JDBC este un API care face parte din standardul limbajului Java și se concentrează pe transferul constructului SQL generat de program în baza de date, precum și pe procesarea rezultatului. SQLJ este un subset al standardului SQL SQL / OLB - pentru acesta conceptul de bază de date este primar, iar limbajul în care sunt incluse constructele SQL este secundar. Conform acestui standard, încorporarea instrucțiunilor SQL este permisă nu numai în Java, ci și în limbajele de programare Ada , C , COBOL , Fortran , MUMPS , PL/I .
Mai mult, utilizarea SQLJ implică de fapt apelarea metodelor JDBC, deoarece în acest caz ele acționează ca un API de nivel înalt și, respectiv, de nivel scăzut . Dacă aprofundați în detaliile implementării tehnologiilor SQLJ și JDBC, puteți constata că orice directive SQLJ sunt traduse în apeluri JDBC în mod transparent pentru programator printr-un subsistem special numit preprocesor SQLJ . Acest lucru vă permite să amestecați în siguranță apelurile SQLJ și JDBC în același fragment de cod, folosind un context comun dacă este necesar.
De fapt, în orice caz particular în care o instrucțiune SQL trebuie să fie executată, alegerea între SQLJ și JDBC ar trebui făcută pe baza naturii operației intenționate. Dacă aceasta este o interogare de căutare complexă, cu posibile variații ale numărului de condiții de căutare, atunci ar fi cu siguranță mai convenabil să se formeze un șir de interogare text și apoi să îl execute prin JDBC; dacă trebuie doar să înlocuiți unele variabile sau expresii calculabile, atunci va fi mai ergonomic în ceea ce privește lungimea codului să scrieți o directivă SQLJ.
Pentru a utiliza eficient inovațiile sintactice introduse de standardul SQLJ, trebuie mai întâi să înțelegeți caracteristicile acestora legate de procesul de analiză a constructelor SQLJ.
Orice construcție SQLJ încep cu directiva #sql, în special, blocurile care conțin interogări SQL în sine sunt specificate ca #sql {…}.
În terminologia SQLJ , o variabilă externă ( eng. variabilă gazdă ) este o variabilă de construcție SQLJ folosită pentru a primi valori sau pentru a le transmite mediului de program extern constructului. De exemplu:
int i , j ; i = 1 ; # sql { SELECT field INTO : OUT j FROM table WHERE id = : IN i }; Sistem . afară . println ( j );Pentru a evita ambiguitățile, variabilele externe trebuie specificate într-o anumită formă, și anume:
:[IN|OUT|INOUT] <имя переменной>.
Modificatorii IN, OUT, sunt INOUTopționali și sunt folosiți pentru a specifica variabile, respectiv, trecând o valoare din exterior către constructul SQLJ; returnând o valoare în exterior și îndeplinind ambele funcții. Aceste cuvinte cheie sunt folosite nu numai pentru aceasta - ele setează și metoda de acces la variabile externe în interiorul constructului SQLJ: dacă există un modificator IN, este posibilă doar citirea valorii variabilei, dacă este prezentă OUT , doar scrierea, dacă este prezentă INOUT , acces complet . În mod implicit (în absența unui modificator specificat explicit), variabilele sunt declarate cu un modificator implicit INOUT.
În loc de doar variabile în constructele SQLJ, puteți utiliza expresii care conțin variabile externe, adesea numite doar expresii externe ( expresii gazdă în engleză ). Au o sintaxă specifică:
:( <выражение> )
Principala nuanță atunci când se utilizează expresii externe este că utilizarea lor poate atrage anumite consecințe legate de faptul că analizarea constructului SQLJ de către preprocesor în prezența mai multor expresii externe se desfășoară într-o anumită ordine, iar atunci când este utilizată în expresiile de atribuire, rezultatul atribuirii poate fi transferat în mediul software.
Pentru a ilustra aceste două puncte, să ne uităm la un exemplu simplu de utilizare a expresiilor externe:
int i = 1 ; # sql { SELECTează rezultatul din tabelul 1 WHERE câmp1 = :( x [ i ++] ) ȘI câmp2 = :( y [ i ++] ) ȘI câmp3 = :( z [ i ++] ) }; Sistem . afară . println ( i );Pe baza experienței de programare, se poate încerca să presupunem asta
Cu toate acestea, atât prima cât și a doua afirmație sunt false. Pentru a verifica acest lucru, să facem o diagramă simplă care clarifică ordinea de analiză a acestui construct de către preprocesorul SQLJ:
i = 1
x[i++] → x[1], i = 2
y[i++] → y[2], i = 3
z[i++] → z[3], i = 4
Prin urmare:
În terminologia SQLJ și JDBC, un context de conexiune este un set de trei parametri care sunt definiți în mod unic de către aceștia:
Pentru orice construct SQLJ, contextul în care va fi executat poate fi definit în mod explicit: #sql [<контекст>] {…}.
În cadrul unei directive #sql, puteți crea și contexte noi pentru utilizare ulterioară: #sql context <контекст>. Dacă contextul nu este setat în mod explicit, atunci constructul este considerat a fi executat în contextul implicit . Dacă este necesar, contextul implicit poate fi schimbat.
Un iterator în terminologia standardului SQLJ este un obiect pentru stocarea rezultatului unei interogări care returnează mai mult de o înregistrare. În esența și implementarea sa, nu este doar un set de înregistrări, ci un set cu o anumită ordine pe el, care permite utilizarea secvenţială a înregistrărilor primite. În acest sens, un iterator are multe în comun cu un cursor .
Standardul oferă două tipuri de iteratoare - diferența dintre ele este destul de interesantă: iteratoarele legate de poziție necesită o sintaxă mai asemănătoare SQL în utilizare, spre deosebire de iteratoarele legate de coloane, care sunt foarte apropiate de obiecte.
Iteratoare legate de pozițiePrimul tip de iterator este iteratorul legat de poziție. Se declară astfel: #sql public iterator ByPos (String, int). Este clar că în acest caz, legarea rezultatelor interogării la un iterator se realizează pur și simplu prin potrivirea tipurilor de date dintre iterator și rezultatul interogării. Cu toate acestea, acest lucru necesită ca tipurile de date ale iteratorului și rezultatul interogării să poată fi mapate între ele în conformitate cu standardul SQL/JRT.
Să creăm un tabel simplu:
CREATE TABLE people ( nume complet VARCHAR ( 50 ), ani de naștere NUMERIC ( 4 , 0 ))Acum, folosind iteratorul de primul tip și construcția , vom FETCH … INTO …prelua date din rezultatul interogării:
Pozitionator bypos ; Nume șir = null ; int an = 0 ; # sql positer = { SELECT full name , birthyyear FROM people }; pentru (;;) { # sql { FETCH : poziter INTO : nume , : anul }; if ( positer . endFetch ()) break ; Sistem . afară . println ( nume + " sa născut în " + an ); }Prima directivă leagă rezultatul interogării la un iterator; al doilea, folosind o construcție FETCH … INTO …, o înregistrare la un moment dat este citită secvențial din rezultat.
Iteratoare cu nume de coloaneAl doilea tip de iterator, mai apropiat de obiectele obișnuite, este iteratorul cu numele de coloană. Pentru tabelul specificat, crearea unui iterator de al doilea tip va arăta astfel:
# sql iterator public ByName ( String fullNAME , int birthYEAR );Este folosit ca obiect obișnuit, și anume, accesul la câmpuri se realizează prin metodele de accesorii corespunzătoare:
Nume ByName ; # sql namiter = { SELECT fullname , birthyyear FROM people }; String s ; int i ; while ( namiter . next ()) { i = namiter . naștereAN (); s = namiter . nume complet (); Sistem . afară . println ( s + " sa născut în " + i ); }Cu toate acestea, există o regulă care trebuie respectată - numele câmpurilor iteratorului trebuie să se potrivească (indiferent între majuscule și minuscule) cu numele câmpurilor din interogare . Acest lucru se datorează procesului de parsare a construcției SQLJ de către preprocesor. Dacă numele unei coloane din baza de date are un nume care este incompatibil cu regulile de denumire a variabilelor în Java, trebuie să utilizați aliasuri în interogarea care formează iteratorul.
Apelurile de procedură sunt foarte ușor de scris folosind variabile externe.
# sql { CALL proc (: myarg )};Funcțiile, la rândul lor, sunt numite folosind constructulVALUE
int i ; # sql i = { VALORI ( func ( 34 ))};Deoarece directivele SQLJ folosesc apeluri JDBC atunci când sunt utilizate, este de interes să puteți utiliza împreună aceste tehnologii. Este destul de ușor să convertiți iteratoarele în obiecte ResultSetși invers.
Transformarea unui obiect ResultSeteste foarte ușoară. Pentru a face acest lucru, trebuie mai întâi să definiți un iterator cu numele coloanelor (în exemplul nostru, acesta va fi notat Employeescu ), apoi să efectuați operația CAST:
# sql iterator Angajații ( String ename , double sal ); PreparedStatement stmt = conn . prepareStatement (); String query = "SELECT ename, sal FROM emp WHERE" ; interogare += undeClause ; ResultSet rs = stmt . executeQuery ( interogare ); Angajati emps ; # sql emps = { CAST : rs }; while ( emps . next ()) { Sistem . afară . println ( emps . ename () + " earns " + emps . sal ()); } emps . închide (); stmt . închide ();Separat, trebuie remarcat faptul că, după legarea rezultatului interogării la iterator, nu este nevoie să închideți separat rezultatul interogării - preprocesorul însuși va face acest lucru pentru programator.
Procesul invers - conversia unui iterator într-un obiect ResultSetse realizează folosind iteratoare de un tip special, așa-numitele iteratoare slab tipate .
sqlj . timpul de rulare . ResultSetIterator iter ; # sql iter = { SELECT ename FROM emp }; ResultSet rs = iter . getResultSet (); while ( rs . next ()) { Sistem . afară . println ( "numele angajatului: " + rs . getString ( 1 )); } iter . închide ();În acest caz, relația dintre iterator și rezultatul interogării este de asemenea păstrată și iteratorul este cel care ar trebui închis.
După cum am menționat mai devreme, cel mai simplu mod de a compara SQLJ ca tehnologie este cu o tehnologie similară orientată spre Java pentru același scop, și anume JDBC. Situația este complicată de faptul că aceste tehnologii nu sunt paralele și nu sunt complet interschimbabile, ci sunt arhitectural una peste alta.
http://www-01.ibm.com/software/data/informix/pubs/library/iif.html
consultați Ghidul utilizatorului Embedded SQLJ