SQLJ

Az SQLJ az SQL - szabvány  egy részhalmaza , amelynek célja az SQL és a Java szintaxis előnyeinek kombinálása az üzleti logika megvalósításának és az adatokkal való munkavégzés kényelmének érdekében. Ezt a szabványt az IBM , Micro Focus , Microsoft , Compaq (pontosabban annak DBMS részlege, amely inkább a felvásárolt Tandem céghez köthető ), Informix , Oracle , Sun és Sybase alkotta konzorcium fejlesztette ki .

Háttér

A JSQL konzorcium (amely később az általa kifejlesztett szabvánnyal azonos nevet kapott) 1997 -es megjelenése idején a relációs DBMS és a Java programok közötti interakció gondolata nem volt új. A JavaSoft ( a Sun leányvállalata) már kifejlesztette a JDK 1.1  megjelenése óta a nyelvi szabványban szereplő JDBC ( Java DataBase Connectivity ) felületet.  Bizonyos okok miatt azonban (lásd SQLJ és JDBC ) az interfész által biztosított szolgáltatások nem voltak elegendőek.

Az SQLJ szabvány specifikációja három részből áll:

1998 végére a specifikáció mindhárom szintje elkészült és benyújtásra került az ANSI - hoz az SQL szabvány kiegészítéseként. Az új szabvány első két része az SQL:1999 szabvány SQL/OLB és SQL/PSM részeibe került ; a harmadik rész külön SQL/JRT modulként került be az SQL:2003 szabványba

Általában az adatbázissal működő alkalmazások fejlesztésével kapcsolatban az SQLJ alatt általában 0-s szintet értünk.

Mintakód

Íme egy egyszerű példa egy Java osztályra, amely SQLJ-t használ az Oracle lekérdezési eredményeinek lekérésére .

import java.sql.* ; import oracle.sqlj.runtime.Oracle ; public class SingleRowQuery extends Base { public static void main ( String [] args ) { try { connect (); singleRowQuery ( 1 ); } catch ( SQLException e ) { e . printStackTrace (); } } public static void singleRowQuery ( int id ) SQLExceptiont dob ​​{ String fullname = null ; String street = null ; # sql { SELECT fullname , street INTO : OUT fullname , : OUT street FROM customer WHERE ID = : IN id }; Rendszer . ki . println ( "Ügyfél azonosítóval = " + id ); Rendszer . ki . println (); Rendszer . ki . println ( teljes név + " " + utca ); } }

A fenti kódból jól látható, hogy egy singleRowQuerySQL-lekérdezés magába az eljárás szövegébe van beágyazva, és ez a beágyazás bizonyos szabályok szerint szerveződik:

  • A kérés szövege az irányelvben található #sql {...};
  • Az SQL-lekérdezésen kívüli változók meghatározott formátumban vannak beállítva

Az alábbiakban minden szintaktikai konstrukciót részletesen tárgyalunk.

SQLJ és JDBC

Logikus, hogy felmerül a kérdés, hogy miért kell két párhuzamos szabványt létrehozni a DBMS hozzáférési technológiák megvalósítására.

Először is érdemes megjegyezni, hogy az SQLJ és a JDBC különböző szabványcsaládokhoz tartoznak, és fogalmilag különböznek egymástól. A JDBC egy olyan API, amely a Java nyelvi szabvány része, és a program által generált SQL-konstrukció adatbázisba átvitelére, valamint az eredmény feldolgozására összpontosít. Az SQLJ az SQL SQL / OLB szabvány egy részhalmaza  - számára az adatbázis fogalma elsődleges, és másodlagos az a nyelv, amelyen az SQL konstrukciók szerepelnek. E szabvány szerint az SQL utasítások beágyazása nem csak Java nyelven engedélyezett, hanem az Ada , C , COBOL , Fortran , MUMPS , PL/I programozási nyelveken is .

Továbbá az SQLJ használata valójában implicit módon magában foglalja a JDBC metódusok meghívását, mivel ebben az esetben ezek magas és alacsony szintű API -ként működnek . Ha belemélyed az SQLJ és a JDBC technológiák megvalósításának részleteibe, láthatja, hogy az SQLJ direktívákat egy speciális alrendszer, az SQLJ előfeldolgozó transzparens módon fordítja le a programozó számára JDBC hívásokká . Ez lehetővé teszi az SQLJ- és JDBC-hívások biztonságos keverését ugyanabban a kódrészletben, szükség esetén közös kontextus használatával.

Valójában minden olyan konkrét esetben, amikor egy SQL utasítást kell végrehajtani, az SQLJ és a JDBC közötti választást a tervezett művelet természete alapján kell meghozni. Ha ez egy összetett keresési lekérdezés a keresési feltételek számának lehetséges eltéréseivel, akkor mindenképpen célszerűbb lenne egy szöveges lekérdezési karakterláncot kialakítani, majd azt JDBC-n keresztül végrehajtani; ha csak néhány változót vagy kiszámítható kifejezést kell helyettesítenie, akkor a kódhossz szempontjából ergonomikusabb lesz egy SQLJ direktívát írni.

Szintaxis

Az SQLJ szabvány által bevezetett szintaktikai újítások hatékony használatához először meg kell értenie az SQLJ konstrukciók elemzési folyamatával kapcsolatos jellemzőit.

Bármely SQLJ-konstrukció a direktívával kezdődik #sql, különösen az SQL-lekérdezéseket tartalmazó blokkok a következővel vannak megadva #sql {…}.

Külső változók

Az SQLJ terminológiában a külső változó ( angol.  host változó ) egy SQLJ konstrukciós változó, amely értékek fogadására vagy a konstrukción kívüli programkörnyezetnek való átadására szolgál. Például:

int i , j ; i = 1 ; # sql { SELECT mező INTO : OUT j FROM table WHERE id = : IN i }; Rendszer . ki . println ( j );

A félreértések elkerülése érdekében a külső változókat meghatározott formában kell megadni, nevezetesen:

:[IN|OUT|INOUT] <имя переменной>.

INA , , módosítók OUTnem INOUTkötelezőek, és változók megadására szolgálnak, amelyek kívülről adnak át értéket az SQLJ konstrukciónak; érték visszaadása kifelé és mindkét funkció végrehajtása. Ezek a kulcsszavak nem csak erre szolgálnak - az SQLJ konstrukción belüli külső változókhoz is beállítják az elérési módot: ha van módosító IN, csak a változó értékének olvasása lehetséges, ha jelen van OUT , csak írás, ha van INOUT , teljes hozzáférés . Alapértelmezés szerint (explicit módon meghatározott módosító hiányában) a változók egy implicit módosítóval deklarálódnak INOUT.

Külső kifejezések

Az SQLJ-konstrukciókban csak változók helyett használhat külső változókat tartalmazó kifejezéseket, amelyeket gyakran csak külső kifejezéseknek neveznek ( angol  host kifejezések ). Meghatározott szintaxisuk van:

:( <выражение> )

A fő árnyalat a külső kifejezések használatánál az, hogy használatuk bizonyos következményekkel járhat azzal kapcsolatban, hogy az SQLJ konstrukció előfeldolgozó általi elemzése több külső kifejezés jelenlétében meghatározott sorrendben történik, és ha hozzárendelési kifejezésekben használjuk, a a feladat eredménye átvihető a szoftverkörnyezetbe.

E két pont szemléltetésére nézzünk egy egyszerű példát a külső kifejezések használatára:

int i = 1 ; # sql { SELECT eredmény FROM table1 WHERE field1 = :( x [ i ++] ) AND field2 = :( y [ i ++] ) AND field3 = :( z [ i ++] ) }; Rendszer . ki . println ( i );

A programozási tapasztalatok alapján megpróbálhatjuk azt feltételezni

  1. A változó értéke inem változik az SQL utasítás elemzése során;
  2. A generált lekérdezés így fog kinézni
SELECT eredmény FROM tábla1 WHERE mező1 = :( x [ 1 ]) ÉS mező2 = :( y [ 1 ]) ÉS mező3 = :( z [ 1 ])

Azonban mind az első, mind a második állítás hamis. Ennek ellenőrzésére készítsünk egy egyszerű diagramot, amely tisztázza a konstrukció SQLJ előfeldolgozó általi elemzésének sorrendjét:

i = 1
x[i++] → x[1], i = 2
y[i++] → y[2], i = 3
z[i++] → z[3], i = 4

Következésképpen:

  1. Az SQLJ direktíva végrehajtása után egy i = 4;
  2. A kérés teljesítve lesz
SELECT eredmény FROM tábla1 WHERE mező1 = :( x [ 1 ]) ÉS mező2 = :( y [ 2 ]) ÉS mező3 = :( z [ 3 ])

Kontextusok

Az SQLJ és a JDBC terminológiában a kapcsolati kontextus három, általuk egyedileg meghatározott paraméter halmaza:

  1. adatbázis név;
  2. munkamenet azonosító;
  3. Az aktív tranzakció azonosítója.

Bármely SQLJ konstrukció esetében kifejezetten meghatározható a kontextus, amelyben végrehajtásra kerül: #sql [<контекст>] {…}.

Egy direktíván belül #sqlúj kontextusokat is létrehozhat későbbi használatra: #sql context <контекст>. Ha a kontextus nincs kifejezetten beállítva, akkor a konstrukciót az alapértelmezett környezetben végrehajtottnak tekintjük .  Ha szükséges, az alapértelmezett környezet módosítható.

Iterátorok

Az SQLJ szabvány terminológiájában az iterátor egy olyan objektum, amely egynél több rekordot visszaadó lekérdezés eredményét tárolja. Lényegében és megvalósításában nem pusztán rekordhalmaz, hanem egy olyan halmaz, amelyen némi rendezettség van, ami lehetővé teszi a kapott rekordok szekvenciális felhasználását. Ebből a szempontból az iterátornak sok közös vonása van a kurzorral .

A szabvány kétféle iterátort biztosít – a különbség köztük meglehetősen érdekes: a pozícióhoz kötött iterátorok használatához SQL-szerűbb szintaxisra van szükség, ellentétben az oszlophoz kötött iterátorokkal, amelyek használatuk során nagyon közel állnak az objektumokhoz.

Pozícióhoz kötött iterátorok

Az első iterátortípus a pozícióhoz kötött iterátor. A következőképpen van kijelentve: #sql public iterator ByPos (String, int). Nyilvánvaló, hogy ebben az esetben a lekérdezési eredmények iterátorhoz rendelése egyszerűen az iterátor és a lekérdezés eredménye közötti adattípusok egyeztetésével történik. Ehhez azonban az szükséges, hogy az iterátor és a lekérdezés eredményének adattípusait le lehessen képezni az SQL/JRT szabvány szerint.

Készítsünk egy egyszerű táblázatot:

TÁBLÁZAT LÉTREHOZÁSA személyek ( teljes név VARCHAR ( 50 ), születési év NUMERIC ( 4 , 0 ) )

Most az első típusú iterátor és a konstrukció FETCH … INTO …segítségével adatokat fogunk lekérni a lekérdezés eredményéből:

Bypos pozicionáló ; Karakterlánc neve = null ; int év = 0 ; # sql positer = { SELECT teljes név , születési év FROM személyek }; for (;;) { # sql { FETCH : positer INTO : name , : year }; if ( positer . endFetch ()) break ; Rendszer . ki . println ( név + " évben született " + évben ); }

Az első direktíva a lekérdezés eredményét egy iterátorhoz köti; a második egy konstrukciót használva FETCH … INTO …egyenként egy rekordot olvas ki az eredményből.

Iterátorok oszlopnevekkel

A második típusú iterátor, amely közelebb áll a szokásos objektumokhoz, az oszlopnevű iterátor. A megadott táblázathoz a második típusú iterátor létrehozása így fog kinézni:

# sql public iterator ByName ( String fullNAME , int birthYEAR );

Rendszeres objektumként használják, nevezetesen a mezőkhöz való hozzáférés a megfelelő hozzáférési metódusokon keresztül történik:

ByName név ; # sql namiter = { SELECT teljes név , születési év FROM személyek }; String s ; int i ; while ( namiter . next ()) { i = namiter . születési ÉV (); s = névadó . teljes NÉV (); Rendszer . ki . println ( s + " " + i -ben született ); }

Van azonban egy szabály, amit be kell tartani – az iterátor mezőinek nevének meg kell egyeznie (a kis- és nagybetűk megkülönböztetése nélkül) a lekérdezésben szereplő mezők nevével . Ez annak köszönhető, hogy az előfeldolgozó feldolgozza az SQLJ-konstrukciót. Ha az adatbázisban lévő oszlop neve nem kompatibilis a Java változók elnevezésére vonatkozó szabályokkal, akkor álneveket kell használnia az iterátort alkotó lekérdezésben.

Eljárások és függvények hívásai

Az eljáráshívásokat nagyon könnyű írni külső változók segítségével.

# sql { CALL proc (: myarg )};

A függvényeket pedig a konstrukció segítségével hívjuk megVALUE

int i ; # sql i = { ÉRTÉKEK ( func ( 34 ))};

Interakció a JDBC-vel

Mivel az SQLJ direktívák használatukkor JDBC-hívásokat használnak, érdemes ezeket a technológiákat együtt használni. Elég egyszerű az iterátorokat objektummá konvertálni ResultSetés fordítva.

Egy objektum átalakítása ResultSetnagyon egyszerű. Ehhez először meg kell határozni egy iterátort az oszlopok nevével (példánkban ezt jelöljük Employees), majd végre kell hajtani a műveletet CAST:

# sql iterator Alkalmazottak ( String ename , double sal ); PreparedStatement stmt = conn . állítás előkészítése (); String query = "SELECT ename, sal FROM emp WHERE " ; query += whereClause ; ResultSet rs = stmt . executeQuery ( lekérdezés ); Alkalmazottak emps ; # sql emps = { CAST : rs }; while ( emps . next ()) { Rendszer . ki . println ( emps . ename ( ) + " keres " + emps . sal ( )); } emps . bezár (); stmt . bezár ();

Külön meg kell jegyezni, hogy a lekérdezés eredményének az iterátorhoz kötése után nem kell külön bezárni a lekérdezés eredményét - ezt maga az előfeldolgozó fogja megtenni a programozó számára.

A fordított folyamat - az iterátor objektummá konvertálása ResultSetspeciális típusú iterátorok, az úgynevezett gyengén típusos iterátorok segítségével történik . 

sqlj . futásidő . ResultSetIterator iter ; # sql iter = { SELECT ename FROM emp }; ResultSet rs = iter . getResultSet (); while ( rs . next ()) { Rendszer . ki . println ( "alkalmazott neve: " + rs . getString ( 1 )); } iter . bezár ();

Ebben az esetben az iterátor és a lekérdezés eredménye közötti kapcsolat is megmarad, és az iterátort kell bezárni.

Az SQLJ jellemzői

Amint azt korábban említettük, az SQLJ-t mint technológiát a legegyszerűbben egy hasonló Java-orientált technológiával, ugyanazzal a céllal lehet összehasonlítani, nevezetesen a JDBC-vel. A helyzetet bonyolítja, hogy ezek a technológiák nem párhuzamosak és nem teljesen felcserélhetők, hanem építészetileg egymásra helyezkednek.

  1. Az azonos célú, JDBC hívásokban és SQLJ direktívában írt lekérdezés a legtöbb esetben a második esetben tömörebben kerül beírásra a programszövegbe, ami csökkenti a lista méretét és az összeállítással kapcsolatos hiba valószínűségét. a végső lekérdezési karakterlánc kis töredékekből;
  2. Bármely SQLJ direktívát az előfeldolgozó elemzi és ellenőrzi a fordítási szakaszban, ezért ebben a szakaszban minden szintaktikai hibát észlel, ellentétben a JDBC-vel, ahol a konstrukciók helyességét csak a Java szintaxis szerint ellenőrzik - már a DBMS a felelős. magának a lekérdezésnek az elemzéséhez és javításához, ami természetesen azt eredményezi, hogy az ilyen jellegű hibákat már az indítási szakaszban észlelik;
  3. Maga az előfeldolgozó (általában nevén sqlj) nem része a JDK -nak ; azt és a működéséhez szükséges könyvtárakat általában a DBMS szállító biztosítja. Ez természetes – amint fentebb látható, az SQLJ sokkal közelebb áll a DBMS-hez, mint magához a Java nyelvhez; továbbá az előfeldolgozónak figyelembe kell vennie "saját" DBMS-e SQL szintaxisának sajátosságait;
  4. A legtöbb esetben, különösen a gyakran végrehajtott összetett lekérdezéseknél, amelyek nagy mennyiségű adattal dolgoznak, az SQLJ direktíva átlagosan gyorsabban hajtódik végre, mint egy hasonló JDBC-hívás. Ez annak a ténynek köszönhető, hogy az SQLJ direktíva esetén a megfelelő lekérdezés terve csak egyszer kerül felépítésre, majd újrafelhasználásra, ellentétben a JDBC-vel, ahol a terv minden hívásra épül;
  5. Az SQLJ direktíva fordítása során készített lekérdezési tervet a felhasználó szükség esetén konfigurálhatja; a JDBC esetében ez nyilvánvaló okokból nem lehetséges;
  6. Ha a lekérdezés minden esetben jelentős változtatásokat igényel (egy egyszerű példa: keresési lekérdezés mezők halmazán, amelyek némelyikéből hiányozhatnak az értékek), akkor egyszerűbb a JDBC használata, mivel az SQLJ használatának nincs előnye;
  7. Mivel a JDBC használatakor nincs szükség további kódfeldolgozási szakaszra - fordításra, a fordítási folyamat ebben az esetben gyorsabb lesz.

Az SQLJ hátrányai

  1. Az SQLJ további előfeldolgozási lépést igényel.
  2. A legtöbb IDE nem támogatja az SQLJ-t.
  3. Az SQLJ-t nem támogatja a legtöbb ORM-keretrendszer, például a Hibernate.

Szoftver támogatás

Oracle

DB/2

Informix

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

lásd: Embedded SQLJ User's Guide

Linkek

  1. Andrew Eisenberg, Jim Melton. Kötések objektumnyelvekhez (holt link) . Letöltve: 2008. november 12. Az eredetiből archiválva : 2011. szeptember 17.. 
  2. Andrew Eisenberg, Jim Melton. SQLJ – 1. rész (nem elérhető hivatkozás) . Letöltve: 2008. november 12. Az eredetiből archiválva : 2009. február 13.. 
  3. IBM Redbooks. DB2 for z/OS és OS/390: Ready for Java (hivatkozás nem érhető el) . Letöltve: 2008. november 12. Az eredetiből archiválva : 2011. augusztus 25.. 
  4. Oracle Database 11g. SQLJ fejlesztői útmutató és referencia (nem elérhető hivatkozás) . Letöltve: 2008. november 12. Az eredetiből archiválva : 2011. augusztus 25..