SQLJ

SQLJ  er en delmængde af SQL - standarden , der har til formål at kombinere fordelene ved SQL og Java -syntaks for at gøre det lettere at implementere forretningslogik og arbejde med data. Denne standard er udviklet af et konsortium bestående af IBM , Micro Focus , Microsoft , Compaq (mere præcist dets DBMS-division, som snarere kan henføres til det opkøbte firma Tandem ), Informix , Oracle , Sun og Sybase .

Baggrund

På tidspunktet for udseendet af JSQL-konsortiet (som senere blev det samme navn med den standard, det udviklede) i 1997, var ideen om interaktion mellem relationelle DBMS og Java-programmer ikke ny. JavaSoft ( et datterselskab af Sun) har allerede udviklet JDBC -grænsefladen ( Java DataBase Connectivity )   inkluderet i sprogstandarden siden udgivelsen af ​​JDK 1.1. Men af ​​visse grunde (se SQLJ og JDBC ) var funktionerne fra denne grænseflade ikke nok.

SQLJ-standardspecifikationen består af tre dele:

Ved udgangen af ​​1998 var alle tre niveauer i specifikationen afsluttet og indsendt til ANSI til overvejelse som tilføjelser til SQL-standarden. De første to dele af den nye standard blev inkluderet i henholdsvis SQL/OLB- og SQL/PSM- delene af SQL:1999- standarden ; den tredje del blev inkluderet som et separat SQL/JRT- modul i SQL:2003 -standarden

Normalt, i forhold til udvikling af applikationer, der arbejder med databasen, forstås SQLJ normalt som niveau 0.

Eksempelkode

Her er et simpelt eksempel på en Java-klasse, der bruger SQLJ til at få forespørgselsresultater fra Oracle .

importer java.sql.* ; import oracle.sqlj.runtime.Oracle ; public class SingleRowQuery udvider Base { public static void main ( String [] args ) { try { connect (); singleRowQuery ( 1 ); } catch ( SQLException e ) { e . printStackTrace (); } } public static void singleRowQuery ( int id ) kaster SQLException { String fullname = null ; Strenggade = null ; _ # sql { SELECT fullname , street INTO : OUT fullname , : OUT street FROM customer WHERE ID = : IN id }; System . ud . println ( "Kunde med ID = " + id ); System . ud . println (); System . ud . println ( fuldt navn + " " + gade ); } }

Fra ovenstående kode er det klart, at en singleRowQuerySQL-forespørgsel er indlejret i teksten til selve proceduren, og denne indlejring er organiseret efter visse regler:

  • Anmodningsteksten er inde i direktivet #sql {...};
  • Variabler, der er eksterne i forhold til SQL-forespørgslen, sættes inde i den i et bestemt format

Alle syntaktiske konstruktioner vil blive diskuteret i detaljer nedenfor.

SQLJ og JDBC

Det er logisk, at spørgsmålet opstår om årsagerne til at skabe to parallelle standarder for implementering af DBMS-adgangsteknologier.

Til at begynde med er det værd at bemærke, at SQLJ og JDBC tilhører forskellige familier af standarder og er konceptuelt forskellige. JDBC er et API, der er en del af Java-sprogstandarden og er fokuseret på at overføre SQL-konstruktionen genereret af programmet til databasen, samt at behandle resultatet. SQLJ er en delmængde af SQL SQL / OLB -standarden  - for den er begrebet en database primært, og det sprog, som SQL-konstruktioner er inkluderet i, er sekundært. Ifølge denne standard er indlejring af SQL-sætninger ikke kun tilladt i Java, men også i programmeringssprogene Ada , C , COBOL , Fortran , MUMPS , PL/I .

Yderligere involverer brugen af ​​SQLJ faktisk implicit at kalde JDBC-metoder, da de i dette tilfælde fungerer som henholdsvis et højt- og lavniveau- API . Hvis du dykker ned i detaljerne omkring implementeringen af ​​SQLJ- og JDBC-teknologier, kan du opdage, at alle SQLJ-direktiver oversættes til JDBC-kald gennemsigtigt for programmøren af ​​et særligt undersystem kaldet SQLJ-præprocessoren . Dette giver dig mulighed for sikkert at blande SQLJ- og JDBC-kald i det samme kodestykke ved at bruge en fælles kontekst, hvis det er nødvendigt.

Faktisk bør valget mellem SQLJ og JDBC i ethvert tilfælde, hvor en SQL-sætning skal udføres, træffes baseret på arten af ​​den påtænkte operation. Hvis dette er en kompleks søgeforespørgsel med mulige variationer i antallet af søgebetingelser, så ville det bestemt være mere hensigtsmæssigt at danne en tekstforespørgselsstreng og derefter udføre den gennem JDBC; hvis du blot skal erstatte nogle variabler eller beregnelige udtryk, så vil det være mere ergonomisk i forhold til kodelængde at skrive et SQLJ-direktiv.

Syntaks

For effektivt at bruge de syntaktiske innovationer introduceret af SQLJ-standarden, skal du først forstå deres funktioner relateret til processen med at parse SQLJ-konstruktioner.

Alle SQLJ-konstruktioner begynder med direktivet #sql, især blokke, der indeholder SQL-forespørgsler, er angivet som #sql {…}.

Eksterne variabler

I SQLJ-terminologi er en ekstern variabel ( eng.  værtsvariabel ) en SQLJ-konstruktionsvariabel, der bruges til at modtage værdier eller videregive dem til programmiljøet uden for konstruktionen. For eksempel:

int i , j ; i = 1 ; # sql { SELECT field INTO : OUT j FRA tabel WHERE id = : IN i }; System . ud . println ( j );

For at undgå uklarheder skal eksterne variable specificeres i en bestemt form, nemlig:

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

Modifikatorerne IN, OUT, er INOUTvalgfrie og bruges til henholdsvis at angive variabler, der overfører en værdi udefra til SQLJ-konstruktionen; returnere en værdi til ydersiden og udføre begge funktioner. Disse nøgleord bruges ikke kun til dette - de indstiller også adgangsmetoden til eksterne variabler inde i SQLJ-konstruktionen: hvis der er en modifikator IN, er det kun muligt at læse værdien af ​​variablen, hvis den er til stede OUT , kun skrivning, hvis den er til stede INOUT , fuld adgang . Som standard (i mangel af en eksplicit specificeret modifikator), er variabler erklæret med en implicit modifikator INOUT.

Ydre udtryk

I stedet for kun variabler i SQLJ-konstruktioner, kan du bruge udtryk, der indeholder eksterne variabler, ofte kaldet blot eksterne udtryk ( engelske  værtsudtryk ). De har en bestemt syntaks:

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

Hovednuancen ved brug af eksterne udtryk er, at deres brug kan have visse konsekvenser relateret til det faktum, at parsingen af ​​SQLJ-konstruktionen af ​​præprocessoren i nærværelse af flere eksterne udtryk forløber i en bestemt rækkefølge, og når de bruges i tildelingsudtryk, resultatet af opgaven kan overføres til softwaremiljøet.

For at illustrere disse to punkter, lad os se på et simpelt eksempel på brug af eksterne udtryk:

int i = 1 ; # sql { SELECT result FROM table1 WHERE field1 = :( x [ i ++] ) AND field2 = :( y [ i ++] ) AND field3 = :( z [ i ++] ) }; System . ud . println ( i );

Baseret på programmeringserfaring kan man forsøge at antage det

  1. Værdien af ​​variablen iændres ikke under parsing af SQL-sætningen;
  2. Den genererede forespørgsel vil se ud
VÆLG resultat FRA tabel1 HVOR felt1 = :( x [ 1 ]) OG felt2 = :( y [ 1 ]) OG felt3 = :( z [ 1 ])

Men både den første og den anden erklæring er falsk. For at kontrollere dette, lad os lave et simpelt diagram, der tydeliggør rækkefølgen af ​​parsing af denne konstruktion af SQLJ-præprocessoren:

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

Følgelig:

  1. Efter udførelse af SQLJ-direktivet vil der være en i = 4;
  2. Anmodningen vil blive udført
VÆLG resultat FRA tabel1 HVOR felt1 = :( x [ 1 ]) OG felt2 = :( y [ 2 ]) OG felt3 = :( z [ 3 ])

Kontekster

I SQLJ- og JDBC-terminologi er en forbindelseskontekst et sæt af tre parametre, der er unikt defineret af dem:

  1. database navn;
  2. session identifikator;
  3. ID for den aktive transaktion.

For enhver SQLJ-konstruktion kan konteksten, som den vil blive udført i, udtrykkeligt defineres: #sql [<контекст>] {…}.

Inden for et direktiv #sqlkan du også oprette nye kontekster til senere brug: #sql context <контекст>. Hvis konteksten ikke er eksplicit angivet, anses konstruktionen for at være udført i standardkonteksten .  Om nødvendigt kan standardkonteksten ændres.

Iteratorer

En iterator i SQLJ-standardens terminologi er et objekt til lagring af resultatet af en forespørgsel, der returnerer mere end én post. I sin essens og implementering er det ikke bare et sæt poster, men et sæt med en vis rækkefølge på, som gør det muligt at bruge de modtagne poster sekventielt. I denne henseende har en iterator meget til fælles med en markør .

Standarden giver to typer iteratorer - forskellen mellem dem er ret interessant: positionsbundne iteratorer kræver en mere SQL-lignende syntaks i brug, i modsætning til kolonnebundne iteratorer, som er meget tæt på objekter i brug.

Positionsbundne iteratorer

Den første type iterator er den positionsbundne iterator. Det erklæres som følger: #sql public iterator ByPos (String, int). Det er klart, at i dette tilfælde udføres bindingen af ​​forespørgselsresultater til en iterator blot ved at matche datatyperne mellem iteratoren og forespørgselsresultatet. Dette kræver dog, at datatyperne for iteratoren og forespørgselsresultatet kan tilknyttes hinanden i henhold til SQL/JRT-standarden.

Lad os lave en simpel tabel:

OPRET TABEL - personer ( fulde navn VARCHAR ( 50 ), fødselsår NUMERISK ( 4 , 0 ))

Nu, ved at bruge iteratoren af ​​den første type og konstruktionen , FETCH … INTO …henter vi data fra forespørgselsresultatet:

Bypos positioner ; Strengnavn = null ; _ int år = 0 ; # sql positer = { SELECT fullname , birthyear FROM people }; for (;;) { # sql { FETCH : positer INTO : name , : year }; if ( positer . endFetch ()) break ; System . ud . println ( navn + " blev født i " + år ); }

Det første direktiv binder forespørgselsresultatet til en iterator; den anden, ved hjælp af en konstruktion FETCH … INTO …, læses én post ad gangen sekventielt fra resultatet.

Iteratorer med kolonnenavne

Den anden type iterator, der er tættere på almindelige objekter, er den kolonnenavngivne iterator. For den angivne tabel vil oprettelse af en iterator af den anden type se sådan ud:

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

Det bruges som et almindeligt objekt, nemlig adgang til felterne udføres gennem de tilsvarende accessormetoder :

Efternavn navn ; # sql namiter = { SELECT fuldt navn , fødselsår FRA personer }; String s ; int i ; while ( namiter . næste ()) { i = namiter . FødselsÅR (); s = namiter . fuldNAVN (); System . ud . println ( s + " blev født i " + i ); }

Der er dog en regel, der skal overholdes - navnene på iteratorfelterne skal matche (uafhængig af store og små bogstaver) med navnene på felterne i forespørgslen . Dette skyldes processen med at parse SQLJ-konstruktionen af ​​præprocessoren. Hvis navnet på en kolonne i databasen har et navn, der er inkompatibelt med reglerne for navngivning af variabler i Java, skal du bruge aliaser i den forespørgsel, der danner iteratoren.

Kald til procedurer og funktioner

Procedurekald er meget nemme at skrive ved hjælp af eksterne variabler.

# sql { CALL proc (: myarg )};

Funktioner kaldes til gengæld ved hjælp af konstruktionenVALUE

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

Interaktion med JDBC

Da SQLJ-direktiver bruger JDBC-kald, når de bruges, er det interessant at kunne bruge disse teknologier sammen. Det er nemt nok at konvertere iteratorer til objekter ResultSetog omvendt.

ResultSetDet er meget nemt at transformere et objekt . For at gøre dette skal du først definere en iterator med navnene på kolonnerne (i vores eksempel vil den blive betegnet Employeesmed ), og derefter udføre operationen CAST:

# sql iterator Medarbejdere ( String ename , double sal ); PreparedStatement stmt = conn . prepareStatement (); String query = "SELECT ename, sal FROM emp WHERE" ; forespørgsel += whereClause ; ResultSet rs = stmt . executeQuery ( forespørgsel ); Medarbejdere emps ; # sql emps = { CAST : rs }; while ( emps . next ()) { System . ud . println ( emps . ename () + " tjener " + emps . sal ()); } ems . lukke (); stmt . lukke ();

Separat skal det bemærkes, at efter at have bindet forespørgselsresultatet til iteratoren, er det ikke nødvendigt at lukke forespørgselsresultatet separat - præprocessoren vil selv gøre dette for programmøren.

Den omvendte proces - konverteringen af ​​en iterator til et objekt ResultSetudføres ved hjælp af iteratorer af en speciel type, de såkaldte svagt typede iteratorer . 

sqlj . køretid . ResultSetIterator iter ; # sql iter = { SELECT ename FROM emp }; ResultSet rs = iter . getResultSet (); while ( rs . next ()) { System . ud . println ( "medarbejdernavn: " + rs . getString ( 1 )); } iter . lukke ();

I dette tilfælde er forholdet mellem iteratoren og resultatet af forespørgslen også bevaret, og det er iteratoren, der skal lukkes.

Funktioner i SQLJ

Som tidligere nævnt er den nemmeste måde at sammenligne SQLJ som teknologi på med en lignende Java-orienteret teknologi til samme formål, nemlig JDBC. Situationen kompliceres af, at disse teknologier ikke er parallelle og ikke fuldstændigt udskiftelige, men arkitektonisk ligger oven på hinanden.

  1. En forespørgsel med samme formål, skrevet i JDBC-kald og i et SQLJ-direktiv, vil i de fleste tilfælde blive skrevet mere kompakt i programteksten i det andet tilfælde, hvilket reducerer størrelsen af ​​listen og sandsynligheden for en fejl i forbindelse med montering den sidste forespørgselsstreng fra små fragmenter;
  2. Ethvert SQLJ-direktiv parses og kontrolleres af præprocessoren på kompileringsstadiet, derfor opdages alle syntaksfejl på dette stadium, i modsætning til JDBC, hvor korrektheden af ​​konstruktioner kun kontrolleres i forhold til Java-syntaks - DBMS er allerede ansvarlig til at analysere og korrigere selve forespørgslen, hvilket naturligvis fører til, at fejl af denne art vil blive opdaget allerede i startfasen;
  3. Selve forprocessoren (normalt kaldet sqlj) er ikke en del af JDK ; det og de biblioteker, der kræves til dets drift, leveres normalt af DBMS-leverandøren. Dette er naturligt - som vist ovenfor er SQLJ meget tættere på DBMS end på selve Java-sproget; desuden skal præprocessoren tage hensyn til de særlige kendetegn ved SQL-syntaksen for "dens" DBMS;
  4. I de fleste tilfælde, især for hyppigt udførte komplekse forespørgsler, der arbejder med store mængder data, vil et SQLJ-direktiv i gennemsnit udføres hurtigere end et lignende sæt JDBC-kald. Dette skyldes, at planen for den tilsvarende forespørgsel i tilfælde af SQLJ-direktivet kun vil blive bygget én gang og derefter genbrugt, i modsætning til JDBC, hvor planen vil blive bygget på hvert opkald;
  5. Forespørgselsplanen, der blev oprettet under oversættelsen af ​​SQLJ-direktivet, kan konfigureres af brugeren, hvis det er nødvendigt; i tilfælde af JDBC er dette af indlysende årsager ikke muligt;
  6. Hvis forespørgslen kræver væsentlige ændringer i hvert enkelt tilfælde (et simpelt eksempel: en søgeforespørgsel på et sæt felter, hvoraf nogle kan have manglende værdier), så er det lettere at bruge JDBC, da der ikke er nogen fordele ved at bruge SQLJ;
  7. Da der ikke er behov for et ekstra trin i kodebehandling - oversættelse ved brug af JDBC, vil kompileringsprocessen i dette tilfælde være hurtigere.

Ulemper ved SQLJ

  1. SQLJ kræver et yderligere forbehandlingstrin.
  2. De fleste IDE'er har ikke SQLJ-understøttelse.
  3. SQLJ understøtter ikke de fleste ORM-frameworks såsom Hibernate.

Softwaresupport

Oracle

DB/2

Informix

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

se Embedded SQLJ Brugervejledning

Links

  1. Andrew Eisenberg, Jim Melton. Bindinger til objektsprog (dødt link) . Hentet 12. november 2008. Arkiveret fra originalen 17. september 2011. 
  2. Andrew Eisenberg, Jim Melton. SQLJ - Del 1 (utilgængeligt link) . Hentet 12. november 2008. Arkiveret fra originalen 13. februar 2009. 
  3. IBM Redbooks. DB2 til z/OS og OS/390: Klar til Java (link ikke tilgængeligt) . Hentet 12. november 2008. Arkiveret fra originalen 25. august 2011. 
  4. Oracle Database 11g. SQLJ Developer's Guide and Reference (utilgængeligt link) . Hentet 12. november 2008. Arkiveret fra originalen 25. august 2011.