SQLJ

SQLJ  on SQL - standardin osajoukko, jonka tarkoituksena on yhdistää SQL:n ja Java -syntaksin edut liiketoimintalogiikan toteuttamisen ja tietojen kanssa työskentelyn helpottamiseksi. Tämän standardin on kehittänyt konsortio, joka koostuu IBM :stä , Micro Focusista , Microsoftista , Compaqista (tarkemmin sanottuna sen DBMS-divisioonasta, joka voi johtua pikemminkin ostetun Tandem -yhtiön ansioista ), Informix , Oracle , Sun ja Sybase .

Tausta

Kun JSQL-konsortio (josta tuli myöhemmin sama nimi sen kehittämän standardin kanssa) ilmestyi vuonna 1997, ajatus relaatiotietokantajärjestelmän ja Java-ohjelmien vuorovaikutuksesta ei ollut uusi. JavaSoft (Sunin tytäryhtiö ) on jo kehittänyt JDBC ( Java DataBase Connectivity )  -rajapinnan  , joka sisältyy kielistandardiin JDK 1.1:n julkaisusta lähtien. Tietyistä syistä (katso SQLJ ja JDBC ) tämän käyttöliittymän tarjoamat ominaisuudet eivät kuitenkaan riittäneet.

SQLJ-standardimääritys koostuu kolmesta osasta:

Vuoden 1998 loppuun mennessä spesifikaation kaikki kolme tasoa oli saatu valmiiksi ja toimitettu ANSI :lle harkittavaksi SQL-standardin lisäyksenä. Uuden standardin kaksi ensimmäistä osaa sisällytettiin SQL:1999 - standardin SQL/OLB- ja SQL/PSM -osiin . kolmas osa sisällytettiin erillisenä SQL/JRT - moduulina SQL:2003 -standardiin

Yleensä tietokannan kanssa toimivien sovellusten kehittämisessä SQLJ ymmärretään yleensä tasona 0.

Esimerkkikoodi

Tässä on yksinkertainen esimerkki Java-luokasta, joka käyttää SQLJ :tä saadakseen kyselytulokset Oraclesta .

tuo java.sql.* ; tuonti oracle.sqlj.runtime.Oracle ; public class SingleRowQuery Extens Base { public static void main ( String [ ] args ) { yritä { yhdistää (); singleRowQuery ( 1 ); } catch ( SQLException e ) { e . printStackTrace (); } } public static void singleRowQuery ( int id ) heittää SQLExceptionin { String fullname = null ; String street = null ; # sql { SELECT fullname , street INTO : OUT fullname , : OUT street FROM customer WHERE ID = : IN id }; Järjestelmä . ulos . println ( "Asiakas, jolla on tunnus = " + id ); Järjestelmä . ulos . println (); Järjestelmä . ulos . println ( koko nimi + " " + katu ); } }

Yllä olevasta koodista on selvää, että singleRowQuerySQL-kysely on upotettu itse menettelyn tekstiin, ja tämä upottaminen on järjestetty tiettyjen sääntöjen mukaan:

  • Pyyntöteksti on direktiivin sisällä #sql {...};
  • SQL-kyselyn ulkopuoliset muuttujat asetetaan sen sisällä tietyssä muodossa

Kaikkia syntaktisia rakenteita käsitellään yksityiskohtaisesti alla.

SQLJ ja JDBC

On loogista, että herää kysymys syistä luoda kaksi rinnakkaista standardia DBMS-pääsytekniikoiden toteuttamiseksi.

Aluksi on syytä huomata, että SQLJ ja JDBC kuuluvat eri standardiperheisiin ja ovat käsitteellisesti erilaisia. JDBC on Java-kielistandardiin kuuluva API, joka keskittyy ohjelman tuottaman SQL-rakenteen siirtämiseen tietokantaan sekä tuloksen käsittelyyn. SQLJ on SQL SQL / OLB -standardin osajoukko  - sille tietokannan käsite on ensisijainen ja kieli, jolla SQL-rakenteet sisällytetään, on toissijainen. Tämän standardin mukaan SQL-lauseiden upottaminen on sallittu paitsi Javassa, myös ohjelmointikielissä Ada , C , COBOL , Fortran , MUMPS , PL/I .

Lisäksi SQLJ:n käyttöön liittyy itse asiassa implisiittisesti JDBC-menetelmien kutsuminen, koska tässä tapauksessa ne toimivat korkean ja matalan tason API :na . Jos perehdyt SQLJ- ja JDBC-tekniikoiden käyttöönoton yksityiskohtiin, voit huomata, että kaikki SQLJ-käskyt muunnetaan ohjelmoijalle läpinäkyvästi JDBC -kutsuiksi erityisellä alijärjestelmällä, jota kutsutaan SQLJ-esiprosessoriksi . Tämän avulla voit turvallisesti sekoittaa SQLJ- ja JDBC-kutsuja samassa koodinpätkässä käyttämällä tarvittaessa yhteistä kontekstia.

Itse asiassa kaikissa erityistapauksissa, joissa SQL-käsky on suoritettava, valinta SQLJ:n ja JDBC:n välillä tulisi tehdä aiotun toiminnon luonteen perusteella. Jos tämä on monimutkainen hakukysely, jossa on mahdollisia vaihteluita hakuehtojen määrässä, olisi ehdottomasti tarkoituksenmukaisempaa muodostaa tekstikyselymerkkijono ja suorittaa se sitten JDBC:n kautta; Jos sinun on vain korvattava joitain muuttujia tai laskettavia lausekkeita, on koodin pituuden kannalta ergonomisempaa kirjoittaa SQLJ-direktiivi.

Syntaksi

Jotta SQLJ-standardin tuomia syntaktisia innovaatioita voidaan käyttää tehokkaasti, sinun on ensin ymmärrettävä niiden ominaisuudet, jotka liittyvät SQLJ-konstruktioiden jäsennysprosessiin.

Kaikki SQLJ-rakenteet alkavat direktiivillä #sql, erityisesti SQL-kyselyjä sisältävät lohkot määritellään nimellä #sql {…}.

Ulkoiset muuttujat

SQLJ-terminologiassa ulkoinen muuttuja ( eng.  host variable ) on SQLJ-konstruktimuuttuja, jota käytetään vastaanottamaan arvoja tai välittämään ne konstruktin ulkopuoliseen ohjelmaympäristöön. Esimerkiksi:

int i , j ; i = 1 ; # sql { VALITSE kenttä INTO : OUT j FROM taulukosta WHERE id = : IN i }; Järjestelmä . ulos . println ( j );

Epäselvyyksien välttämiseksi ulkoiset muuttujat on määritettävä tietyssä muodossa, nimittäin:

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

Muuttimet IN, OUT, ovat INOUTvalinnaisia ​​ja niitä käytetään määrittämään muuttujia, vastaavasti, siirtäen arvon ulkopuolelta SQLJ-konstruktioon; palauttaa arvon ulkopuolelle ja suorittaa molemmat toiminnot. Näitä avainsanoja ei käytetä vain tähän - ne asettavat myös pääsytavan ulkoisille muuttujille SQLJ-konstruktion sisällä: jos on modifier IN, vain muuttujan arvon lukeminen on mahdollista, jos läsnä OUT , vain kirjoittaminen, jos läsnä INOUT , täysi pääsy . Oletusarvoisesti (jos eksplisiittisesti määritettyä muuntajaa ei ole) muuttujat ilmoitetaan implisiittisellä modifioijalla INOUT.

Ulkoiset ilmaisut

SQLJ-konstruktioiden pelkkien muuttujien sijaan voit käyttää ulkoisia muuttujia sisältäviä lausekkeita, joita usein kutsutaan vain ulkoisiksi lausekkeiksi ( englanniksi  isäntälausekkeet ). Niillä on erityinen syntaksi:

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

Pääasiallinen vivahde ulkoisten lausekkeiden käytössä on se, että niiden käytöllä voi olla tiettyjä seurauksia, jotka liittyvät siihen, että esiprosessorin suorittama SQLJ-konstruktin jäsennys useiden ulkoisten lausekkeiden läsnä ollessa etenee tietyssä järjestyksessä, ja kun sitä käytetään määrityslausekkeissa, toimeksiannon tulos voidaan siirtää ohjelmistoympäristöön.

Näiden kahden kohdan havainnollistamiseksi katsotaanpa yksinkertaista esimerkkiä ulkoisten lausekkeiden käytöstä:

int i = 1 ; # sql { VALITSE tulos taulukosta1, MISSÄ kenttä1 = :( x [ i ++] ) JA kenttä2 = :( y [ i ++] ) AND kenttä3 = :( z [ i ++] ) } ; Järjestelmä . ulos . println ( i );

Ohjelmointikokemuksen perusteella sitä voidaan yrittää olettaa

  1. Muuttujan arvo iei muutu SQL-käskyn jäsentämisen aikana;
  2. Luotu kysely näyttää tältä
VALITSE tulos taulukosta 1 WHERE kenttä1 = :( x [ 1 ] ) JA kenttä2 = :( y [ 1 ] ) AND kenttä3 = :( z [ 1 ] )

Sekä ensimmäinen että toinen väite ovat kuitenkin vääriä. Tämän tarkistamiseksi tehdään yksinkertainen kaavio, joka selventää tämän rakenteen jäsennysjärjestystä SQLJ-esiprosessorilla:

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

Näin ollen:

  1. SQLJ-direktiivin suorittamisen jälkeen tulee i = 4;
  2. Pyyntö toteutetaan
VALITSE tulos taulukosta 1 WHERE kenttä1 = :( x [ 1 ] ) JA kenttä2 = :( y [ 2 ] ) AND kenttä3 = :( z [ 3 ] )

Kontekstit

SQLJ- ja JDBC-terminologiassa yhteyskonteksti on joukko kolmea parametria, jotka ne määrittelevät yksilöllisesti :

  1. tietokannan nimi;
  2. istunnon tunniste;
  3. Aktiivisen tapahtuman tunnus.

Jokaiselle SQLJ-konstruktille konteksti, jossa se suoritetaan, voidaan määrittää erikseen: #sql [<контекст>] {…}.

Direktiivin sisällä #sqlvoit myös luoda uusia konteksteja myöhempää käyttöä varten: #sql context <контекст>. Jos kontekstia ei ole erikseen asetettu, konstruktin katsotaan suoritetuksi oletuskontekstissa .  Tarvittaessa oletuskontekstia voidaan muuttaa.

Iteraattorit

Iteraattori SQLJ-standardin terminologiassa on objekti, joka tallentaa useamman kuin yhden tietueen palauttavan kyselyn tuloksen. Pohjimmiltaan ja toteutuksessaan se ei ole vain tietuejoukko, vaan joukko jossa on jonkin verran järjestystä, joka mahdollistaa vastaanotettujen tietueiden peräkkäisen käytön. Tässä suhteessa iteraattorilla on paljon yhteistä kursorin kanssa .

Standardi tarjoaa kahdenlaisia ​​iteraattoreita - niiden välinen ero on varsin mielenkiintoinen: paikkasidottu iteraattorit vaativat käytössään enemmän SQL:n kaltaista syntaksia, toisin kuin sarakesidottu iteraattorit, jotka ovat käytössä hyvin lähellä objekteja.

Paikointisidotut iteraattorit

Ensimmäinen iteraattorityyppi on paikkasidottu iteraattori. Se ilmoitetaan seuraavasti: #sql public iterator ByPos (String, int). On selvää, että tässä tapauksessa kyselytulosten sitominen iteraattoriin suoritetaan yksinkertaisesti sovittamalla tietotyypit iteraattorin ja kyselyn tuloksen välillä. Tämä edellyttää kuitenkin, että iteraattorin ja kyselytuloksen tietotyypit voidaan yhdistää toisiinsa SQL/JRT-standardin mukaisesti.

Luodaan yksinkertainen taulukko:

CREATE TABLE ihmiset ( koko nimi VARCHAR ( 50 ), syntymävuosi NUMEROT ( 4 , 0 ) )

Nyt käyttämällä ensimmäisen tyypin iteraattoria ja rakennetta , haemme FETCH … INTO …tiedot kyselyn tuloksesta:

Bypos asennoitin ; Merkkijonon nimi = null ; int vuosi = 0 ; # sql positer = { VALITSE koko nimi , syntymävuosi FROM ihmisiltä }; for (;;) { # sql { FETCH : positer INTO : name , : year }; if ( positer . endFetch ()) break ; Järjestelmä . ulos . println ( nimi + " syntyi vuonna " + vuosi ); }

Ensimmäinen direktiivi sitoo kyselyn tuloksen iteraattoriin; toinen, käyttämällä konstruktiota FETCH … INTO …, yksi tietue kerrallaan luetaan tuloksesta peräkkäin.

Iteraattorit sarakkeiden nimillä

Toinen iteraattorityyppi, joka on lähempänä tavallisia objekteja, on sarakeniminen iteraattori. Määritetylle taulukolle toisen tyypin iteraattorin luominen näyttää tältä:

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

Sitä käytetään tavallisena objektina, nimittäin pääsy kenttiin tapahtuu vastaavien apuvälinemenetelmien kautta :

ByName nimi ; # sql namiter = { VALITSE koko nimi , syntymävuosi FROM ihmisiltä }; merkkijono s ; int i ; while ( namiter . next ( )) { i = namiteri . syntymäYEAR (); s = nimimerkki . fullNAME (); Järjestelmä . ulos . println ( s + " syntyi " + i ); }

On kuitenkin olemassa sääntö, jota on noudatettava - iteraattorikenttien nimien tulee vastata (kirjainkoolla ei väliä) kyselyn kenttien nimien kanssa . Tämä johtuu esiprosessorin suorittamasta SQLJ-rakenteen jäsennysprosessista. Jos tietokannan sarakkeen nimessä on nimi, joka ei ole yhteensopiva Java-muuttujien nimeämissääntöjen kanssa, sinun on käytettävä aliaksia iteraattorin muodostavassa kyselyssä.

Proseduurien ja toimintojen kutsut

Proseduurikutsut on erittäin helppo kirjoittaa ulkoisten muuttujien avulla.

# sql { CALL proc (: myarg )};

Funktioita puolestaan ​​kutsutaan konstruktia käyttämälläVALUE

int i ; # sql i = { ARVOT ( func ( 34 ))};

Vuorovaikutus JDBC:n kanssa

Koska SQLJ-direktiivit käyttävät JDBC-kutsuja, kun niitä käytetään, on mielenkiintoista pystyä käyttämään näitä tekniikoita yhdessä. Iteraattorit on tarpeeksi helppoa muuntaa objekteiksi ResultSetja päinvastoin.

Objektin muuntaminen ResultSeton erittäin helppoa. Tätä varten sinun on ensin määritettävä iteraattori sarakkeiden nimillä (esimerkissämme se merkitään Employees), ja suorita sitten toiminto CAST:

# sql iterator Työntekijät ( String ename , double sal ); PreparedStatement stmt = conn . valmisteleLaumaus (); String query = "SELECT ename, sal FROM emp WHERE" ; kysely += whereClause ; ResultSet rs = stmt . executeQuery ( kysely ); Työntekijät emps ; # sql emps = { CAST : rs }; while ( emps . next ( )) { System . ulos . println ( emps . ename () + " ansaitsee " + emps . sal ()); } emps . sulje (); stmt . sulje ();

Erikseen tulee huomioida, että kun kyselytulos on sidottu iteraattoriin, kyselyn tulosta ei tarvitse erikseen sulkea - esiprosessori itse tekee sen ohjelmoijan puolesta.

Käänteinen prosessi - iteraattorin muuntaminen objektiksi ResultSetsuoritetaan käyttämällä erityistyyppisiä iteraattoreita, niin sanottuja heikosti tyypitettyjä iteraattoreita . 

sqlj . ajoaika . ResultSetIterator iter ; # sql iter = { VALITSE ename FROM emp }; ResultSet rs = iter . getResultSet (); while ( rs . next ( )) { System . ulos . println ( "työntekijän nimi: " + rs . getString ( 1 )); } iter . sulje ();

Tässä tapauksessa myös iteraattorin ja kyselyn tuloksen välinen suhde säilyy ja iteraattori tulee sulkea.

SQLJ:n ominaisuudet

Kuten aiemmin mainittiin, helpoin tapa verrata SQLJ:tä teknologiana on samankaltainen Java-suuntautunut tekniikka samaan tarkoitukseen, nimittäin JDBC. Tilannetta mutkistaa se, että nämä tekniikat eivät ole rinnakkaisia ​​eivätkä täysin vaihdettavissa keskenään, vaan ovat arkkitehtonisesti päällekkäisiä.

  1. Saman tarkoituksen kysely, joka on kirjoitettu JDBC-kutsuissa ja SQLJ-direktiivissä, kirjoitetaan useimmissa tapauksissa tiiviimmin ohjelmatekstiin toisessa tapauksessa, mikä vähentää listauksen kokoa ja kokoamiseen liittyvän virheen todennäköisyyttä. lopullinen kyselymerkkijono pienistä fragmenteista;
  2. Esiprosessori jäsentää ja tarkistaa kaikki SQLJ-direktiivit käännösvaiheessa, joten kaikki syntaksivirheet havaitaan tässä vaiheessa, toisin kuin JDBC:ssä, jossa rakennusten oikeellisuutta valvotaan vain Java-syntaksin suhteen - DBMS on jo vastuussa. itse kyselyn jäsentämiseen ja korjaamiseen, mikä luonnollisesti johtaa siihen, että tällaiset virheet havaitaan jo käynnistysvaiheessa;
  3. Itse esiprosessori (yleensä nimeltään sqlj) ei ole osa JDK :ta ; sen ja sen toimintaan tarvittavat kirjastot tarjoavat yleensä DBMS-toimittaja. Tämä on luonnollista - kuten yllä näkyy, SQLJ on paljon lähempänä DBMS:ää kuin itse Java-kieltä; lisäksi esiprosessorin on otettava huomioon "sen" DBMS:n SQL-syntaksin erityispiirteet;
  4. Useimmissa tapauksissa, erityisesti usein suoritetuissa monimutkaisissa kyselyissä, jotka toimivat suurilla tietomäärillä, SQLJ-käsky suorittaa keskimäärin nopeammin kuin vastaava JDBC-kutsujen joukko. Tämä johtuu siitä, että SQLJ-direktiivin tapauksessa vastaavan kyselyn suunnitelma rakennetaan vain kerran ja sen jälkeen käytetään uudelleen, toisin kuin JDBC:ssä, jossa suunnitelma rakennetaan jokaiselle kutsulle;
  5. Käyttäjä voi tarvittaessa konfiguroida SQLJ-direktiivin käännöksen aikana luodun kyselysuunnitelman; JDBC:n tapauksessa tämä ei ole ilmeisistä syistä mahdollista;
  6. Jos kysely vaatii merkittäviä muutoksia kussakin tapauksessa (yksinkertainen esimerkki: hakukysely kentillä, joista joistakin voi puuttua arvoja), on JDBC:tä helpompi käyttää, koska SQLJ:n käytöstä ei ole etua;
  7. Koska JDBC:tä käytettäessä ei tarvita ylimääräistä koodinkäsittelyvaihetta - käännöstä, käännösprosessi on tässä tapauksessa nopeampi.

SQLJ:n haitat

  1. SQLJ vaatii ylimääräisen esikäsittelyvaiheen.
  2. Useimmilla IDE:illä ei ole SQLJ-tukea.
  3. SQLJ:llä ei ole tukea useimmissa ORM-kehyksissä, kuten Hibernate.

Ohjelmistotuki

Oracle

DB/2

Informix

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

katso Embedded SQLJ User's Guide

Linkit

  1. Andrew Eisenberg, Jim Melton. Objektikielien sidokset (kuollut linkki) . Haettu 12. marraskuuta 2008. Arkistoitu alkuperäisestä 17. syyskuuta 2011. 
  2. Andrew Eisenberg, Jim Melton. SQLJ - Osa 1 (linkki ei saatavilla) . Haettu 12. marraskuuta 2008. Arkistoitu alkuperäisestä 13. helmikuuta 2009. 
  3. IBM Redbooks. DB2 for z/OS ja OS/390: Ready for Java (linkki ei saatavilla) . Haettu 12. marraskuuta 2008. Arkistoitu alkuperäisestä 25. elokuuta 2011. 
  4. Oracle Database 11g. SQLJ Developer's Guide and Reference (linkki ei saatavilla) . Haettu 12. marraskuuta 2008. Arkistoitu alkuperäisestä 25. elokuuta 2011.