SQLJ

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 .

Fundal

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.

Exemplu de cod

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:

  • Textul cererii se află în interiorul directivei #sql {...};
  • Variabilele externe interogării SQL sunt setate în interiorul acesteia într-un format specific

Toate construcțiile sintactice vor fi discutate în detaliu mai jos.

SQLJ și JDBC

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.

Sintaxă

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 {…}.

Variabile externe

Î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.

Expresii exterioare

Î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

  1. Valoarea variabilei inu se va modifica în timpul parsării instrucțiunii SQL;
  2. Interogarea generată va arăta ca
SELECTAȚI rezultatul din tabelul1 WHERE câmp1 = :( x [ 1 ]) ȘI câmp2 = :( y [ 1 ]) ȘI câmp3 = :( z [ 1 ] )

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:

  1. După executarea directivei SQLJ, va exista un i = 4;
  2. Cererea va fi executată
SELECTAȚI rezultatul din tabelul1 WHERE câmp1 = :( x [ 1 ]) ȘI câmp2 = :( y [ 2 ]) ȘI câmp3 = :( z [ 3 ] )

Contexte

Î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:

  1. numele bazei de date;
  2. identificatorul de sesiune;
  3. ID-ul tranzacției active.

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.

Iteratori

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ție

Primul 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 coloane

Al 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.

Apeluri la proceduri și funcții

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 ))};

Interacțiune cu JDBC

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.

Caracteristicile SQLJ

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.

  1. O interogare cu același scop, scrisă în apeluri JDBC și într-o directivă SQLJ, în cele mai multe cazuri va fi scrisă mai compact în textul programului în al doilea caz, ceea ce reduce dimensiunea listării și probabilitatea unei erori asociate cu asamblarea. șirul de interogare final din fragmente mici;
  2. Orice directivă SQLJ este analizată și verificată de preprocesor în etapa de compilare, prin urmare, toate erorile de sintaxă sunt detectate în această etapă, spre deosebire de JDBC, unde corectitudinea construcțiilor este controlată doar în ceea ce privește sintaxa Java - DBMS este deja responsabil pentru analizarea și corectarea interogării în sine, ceea ce, desigur, duce la faptul că erorile de acest fel vor fi detectate deja în faza de lansare;
  3. Preprocesorul în sine (denumit de obicei sqlj) nu face parte din JDK ; acesta și bibliotecile necesare pentru funcționarea sa sunt de obicei furnizate de furnizorul DBMS. Acest lucru este firesc - așa cum se arată mai sus, SQLJ este mult mai aproape de DBMS decât de limbajul Java în sine; în plus, preprocesorul trebuie să țină cont de particularitățile sintaxei SQL a SGBD-ului „sau”;
  4. În majoritatea cazurilor, în special pentru interogările complexe executate frecvent care funcționează cu cantități mari de date, o directivă SQLJ se va executa în medie mai rapid decât un set similar de apeluri JDBC. Acest lucru se datorează faptului că planul pentru interogarea corespunzătoare în cazul directivei SQLJ va fi construit o singură dată și apoi reutilizat, spre deosebire de JDBC, unde planul va fi construit la fiecare apel;
  5. Planul de interogare creat în timpul traducerii directivei SQLJ poate fi configurat de utilizator dacă este necesar; în cazul JDBC, din motive evidente, acest lucru nu este posibil;
  6. Dacă interogarea necesită modificări semnificative în fiecare caz (un exemplu simplu: o interogare de căutare pe un set de câmpuri, dintre care unele pot avea valori lipsă), atunci este mai ușor să utilizați JDBC, deoarece nu există avantaje în utilizarea SQLJ;
  7. Deoarece atunci când utilizați JDBC nu este nevoie de o etapă suplimentară de procesare a codului - traducere, procesul de compilare în acest caz va fi mai rapid.

Dezavantajele SQLJ

  1. SQLJ necesită un pas suplimentar de preprocesare.
  2. Majoritatea IDE-urilor nu au suport SQLJ.
  3. SQLJ nu are suport în majoritatea cadrelor ORM, cum ar fi Hibernate.

Suport software

Oracle

DB/2

Informix

http://www-01.ibm.com/software/data/informix/pubs/library/iif.html

consultați Ghidul utilizatorului Embedded SQLJ

Link -uri

  1. Andrew Eisenberg, Jim Melton. Legături pentru limbaje obiect (link mort) . Consultat la 12 noiembrie 2008. Arhivat din original la 17 septembrie 2011. 
  2. Andrew Eisenberg, Jim Melton. SQLJ - Partea 1 (link indisponibil) . Consultat la 12 noiembrie 2008. Arhivat din original la 13 februarie 2009. 
  3. IBM Redbooks. DB2 pentru z/OS și OS/390: Gata pentru Java (link nu este disponibil) . Consultat la 12 noiembrie 2008. Arhivat din original la 25 august 2011. 
  4. Baza de date Oracle 11g. Ghidul și referința pentru dezvoltatori SQLJ (link indisponibil) . Consultat la 12 noiembrie 2008. Arhivat din original la 25 august 2011.