SQLJ

SQLJ  är en delmängd av SQL- standarden , som syftar till att kombinera fördelarna med SQL och Java -syntax för bekvämligheten med att implementera affärslogik och arbeta med data. Denna standard utvecklades av ett konsortium bestående av IBM , Micro Focus , Microsoft , Compaq (närmare bestämt dess DBMS-division, som snarare kan hänföras till det förvärvade företaget Tandem ), Informix , Oracle , Sun och Sybase .

Bakgrund

Vid tiden för uppkomsten av JSQL-konsortiet (som senare blev samma namn med standarden som det utvecklade) 1997, var idén om interaktion mellan relationella DBMS och Java-program inte ny. JavaSoft ( ett dotterbolag till Sun) har redan utvecklat JDBC -gränssnittet ( Java DataBase Connectivity )  som  ingår i språkstandarden sedan lanseringen av JDK 1.1. Av vissa skäl (se SQLJ och JDBC ) räckte dock inte funktionerna i detta gränssnitt.

SQLJ-standardspecifikationen består av tre delar:

I slutet av 1998 hade alla tre nivåerna i specifikationen slutförts och överlämnats till ANSI för övervägande som tillägg till SQL-standarden. De två första delarna av den nya standarden inkluderades i SQL/OLB respektive SQL/PSM -delarna av SQL:1999 - standarden ; den tredje delen ingick som en separat SQL/JRT -modul i SQL:2003 -standarden

Vanligtvis, i förhållande till utvecklingen av applikationer som fungerar med databasen, förstås SQLJ vanligtvis som nivå 0.

Exempelkod

Här är ett enkelt exempel på en Java-klass som använder SQLJ för att få frågeresultat från Oracle .

importera java.sql.* ; importera oracle.sqlj.runtime.Oracle ; public class SingleRowQuery utökar Base { public static void main ( String [] args ) { try { connect (); singleRowQuery ( 1 ); } catch ( SQLException e ) { e . printStackTrace (); } } public static void singleRowQuery ( int id ) kastar SQLException { String fullname = null ; String street = null ; # sql { SELECT fullname , street INTO : OUT fullname , : OUT street FROM customer WHERE ID = : IN id }; System . ut . println ( "Kund med ID = " + id ); System . ut . println (); System . ut . println ( fullnamn + " " + gata ); } }

Från ovanstående kod är det tydligt att en singleRowQuerySQL-fråga är inbäddad i texten i själva proceduren, och denna inbäddning är organiserad enligt vissa regler:

  • Begärans text finns i direktivet #sql {...};
  • Variabler utanför SQL-frågan ställs in i den i ett specifikt format

Alla syntaktiska konstruktioner kommer att diskuteras i detalj nedan.

SQLJ och JDBC

Det är logiskt att frågan uppstår om skälen till att skapa två parallella standarder för att implementera DBMS-accessteknologier.

Till att börja med är det värt att notera att SQLJ och JDBC tillhör olika familjer av standarder och är konceptuellt olika. JDBC är ett API som ingår i Java-språkstandarden och fokuserar på att överföra SQL-konstruktionen som genereras av programmet till databasen, samt bearbeta resultatet. SQLJ är en delmängd av SQL SQL / OLB- standarden  - för den är begreppet en databas primär, och språket som SQL-konstruktioner ingår i är sekundärt. Enligt denna standard är inbäddning av SQL-satser tillåten inte bara i Java, utan även i programmeringsspråken Ada , C , COBOL , Fortran , MUMPS , PL/I .

Vidare innebär användningen av SQLJ faktiskt implicit anrop av JDBC-metoder, eftersom de i det här fallet fungerar som ett hög- respektive lågnivå- API . Om du fördjupar dig i detaljerna kring implementeringen av SQLJ- och JDBC-teknologier kan du upptäcka att alla SQLJ-direktiv översätts till JDBC-anrop transparent för programmeraren av ett speciellt undersystem som kallas SQLJ-förprocessorn . Detta gör att du säkert kan blanda SQLJ- och JDBC-anrop i samma kodavsnitt, med hjälp av en gemensam kontext om det behövs.

Faktum är att i ett särskilt fall där en SQL-sats behöver köras, bör valet mellan SQLJ och JDBC göras baserat på arten av den avsedda operationen. Om detta är en komplex sökfråga med möjliga variationer i antalet sökvillkor, skulle det definitivt vara mer ändamålsenligt att skapa en textfrågesträng och sedan exekvera den genom JDBC; om du bara behöver ersätta några variabler eller beräkningsbara uttryck, så blir det mer ergonomiskt när det gäller kodlängd att skriva ett SQLJ-direktiv.

Syntax

För att effektivt kunna använda de syntaktiska innovationer som introduceras av SQLJ-standarden måste du först förstå deras funktioner som är relaterade till processen för att analysera SQLJ-konstruktioner.

Alla SQLJ-konstruktioner börjar med direktivet #sql, i synnerhet block som innehåller SQL-frågor anges som #sql {…}.

Externa variabler

I SQLJ-terminologi är en extern variabel ( eng.  hostvariabel ) en SQLJ-konstruktionsvariabel som används för att ta emot värden eller skicka dem till programmiljön utanför konstruktionen. Till exempel:

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

För att undvika oklarheter måste externa variabler specificeras i en viss form, nämligen:

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

Modifierarna IN, OUT, är INOUTvalfria och används för att specificera respektive variabler, som skickar ett värde utifrån till SQLJ-konstruktionen; returnera ett värde till utsidan och utföra båda funktionerna. Dessa nyckelord används inte bara för detta - de ställer också in åtkomstmetoden till externa variabler inuti SQLJ-konstruktionen: om det finns en modifierare INär det bara möjligt att läsa värdet på variabeln, om det finns OUT , bara skriva, om det finns INOUT , full åtkomst . Som standard (i avsaknad av en explicit specificerad modifierare) deklareras variabler med en implicit modifierare INOUT.

Yttre uttryck

Istället för bara variabler i SQLJ-konstruktioner kan du använda uttryck som innehåller externa variabler, ofta kallade bara externa uttryck ( engelska  host expressions ). De har en specifik syntax:

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

Huvudnyansen när man använder externa uttryck är att deras användning kan medföra vissa konsekvenser relaterade till det faktum att förprocessorns analys av SQLJ-konstruktionen i närvaro av flera externa uttryck fortskrider i en viss ordning, och när de används i tilldelningsuttryck, resultatet av uppdraget kan överföras till mjukvarumiljön.

För att illustrera dessa två punkter, låt oss titta på ett enkelt exempel på användning av externa uttryck:

int i = 1 ; # sql { VÄLJ resultat FRÅN tabell1 WHERE field1 = :( x [ i ++ ] ) AND field2 = :( y [ i ++] ) AND field3 = :( z [ i ++] ) }; System . ut . println ( i );

Baserat på erfarenhet av programmering kan man försöka anta det

  1. Variabelns värde ikommer inte att ändras under analysen av SQL-satsen;
  2. Den genererade frågan kommer att se ut
VÄLJ resultat FRÅN tabell1 VAR fält1 = :( x [ 1 ]) OCH fält2 = :( y [ 1 ]) OCH fält3 = :( z [ 1 ])

Men både det första och det andra påståendet är falskt. För att kontrollera detta, låt oss göra ett enkelt diagram som klargör ordningen för att analysera denna konstruktion av SQLJ-förprocessorn:

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

Följaktligen:

  1. Efter att ha kört SQLJ-direktivet kommer det att finnas en i = 4;
  2. Begäran kommer att verkställas
VÄLJ resultat FRÅN tabell1 VAR fält1 = :( x [ 1 ]) OCH fält2 = :( y [ 2 ]) OCH fält3 = :( z [ 3 ])

Kontexter

I SQLJ- och JDBC-terminologi är en anslutningskontext en uppsättning av tre parametrar som är unikt definierade av dem:

  1. Databas namn;
  2. sessionsidentifierare;
  3. ID för den aktiva transaktionen.

För alla SQLJ-konstruktioner kan kontexten i vilken den kommer att köras definieras uttryckligen: #sql [<контекст>] {…}.

Inom ett direktiv #sqlkan du också skapa nya sammanhang för senare användning: #sql context <контекст>. Om kontexten inte är explicit inställd, anses konstruktionen vara exekverad i standardkontexten .  Vid behov kan standardkontexten ändras.

Iteratorer

En iterator i SQLJ-standardens terminologi är ett objekt för att lagra resultatet av en fråga som returnerar mer än en post. I sin essens och implementering är det inte bara en uppsättning poster, utan en uppsättning med viss ordning på den, vilket gör det möjligt att använda de mottagna posterna sekventiellt. I detta avseende har en iterator mycket gemensamt med en markör .

Standarden tillhandahåller två typer av iteratorer - skillnaden mellan dem är ganska intressant: positionsbundna iteratorer kräver en mer SQL-liknande syntax vid användning, till skillnad från kolumnbundna iteratorer, som är mycket nära objekt i användning.

Positionsbundna iteratorer

Den första typen av iterator är den positionsbundna iteratorn. Det deklareras enligt följande: #sql public iterator ByPos (String, int). Det är tydligt att i det här fallet utförs bindningen av frågeresultat till en iterator helt enkelt genom att matcha datatyperna mellan iteratorn och frågeresultatet. Detta kräver dock att datatyperna för iteratorn och frågeresultatet kan mappas till varandra enligt SQL/JRT-standarden.

Låt oss skapa en enkel tabell:

SKAPA TABELL personer ( fullständigt namn VARCHAR ( 50 ) , födelseår NUMERISK ( 4 , 0 ))

Nu, med hjälp av iteratorn av den första typen och konstruktionen , FETCH … INTO …hämtar vi data från frågeresultatet:

Bypos positioner ; Strängnamn = null ; _ int år = 0 ; # sql positer = { SELECT fullname , birthyear FROM people } ; för (;;) { # sql { FETCH : positer INTO : name , : year }; if ( positer . endFetch ()) break ; System . ut . println ( namn + " föddes i " + år ); }

Det första direktivet binder frågeresultatet till en iterator; den andra, med hjälp av en konstruktion FETCH … INTO …, läses en post åt gången från resultatet.

Iteratorer med kolumnnamn

Den andra typen av iterator, som är närmare vanliga objekt, är den kolumnnamnade iteratorn. För den angivna tabellen kommer att skapa en iterator av den andra typen att se ut så här:

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

Det används som ett vanligt objekt, nämligen åtkomst till fälten utförs genom motsvarande åtkomstmetoder :

ByName namn ; # sql namiter = { SELECT fullname , birthyear FROM people } ; Sträng s ; int i ; while ( namiter . nästa ()) { i = namiter . födelseÅR (); s = namiter . fullNAME (); System . ut . println ( s + " föddes i " + i ); }

Det finns dock en regel som måste följas - namnen på iteratorfälten måste matcha (okänsliga skiftlägen) med namnen på fälten i frågan . Detta beror på processen att analysera SQLJ-konstruktionen av förprocessorn. Om namnet på en kolumn i databasen har ett namn som är inkompatibelt med reglerna för namngivning av variabler i Java, måste du använda alias i frågan som utgör iteratorn.

Anrop till procedurer och funktioner

Proceduranrop är mycket lätta att skriva med hjälp av externa variabler.

# sql { CALL proc (: myarg )};

Funktioner kallas i sin tur med hjälp av konstruktionenVALUE

int i ; # sql i = { VÄRDEN ( func ( 34 ))};

Interaktion med JDBC

Eftersom SQLJ-direktiv använder JDBC-anrop när de används, är det av intresse att kunna använda dessa teknologier tillsammans. Det är tillräckligt enkelt att konvertera iteratorer till objekt ResultSetoch vice versa.

Att transformera ett objekt ResultSetär väldigt enkelt. För att göra detta måste du först definiera en iterator med namnen på kolumnerna (i vårt exempel kommer den att betecknas Employeesmed ), och sedan utföra operationen CAST:

# sql iterator Anställda ( String ename , double sal ); PreparedStatement stmt = anslutning . prepareStatement (); String query = "SELECT ename, sal FROM emp WHERE" ; fråga += whereClause ; ResultSet rs = stmt . executeQuery ( fråga ); Anställda emps ; # sql emps = { CAST : rs }; while ( emps . next ()) { System . ut . println ( emps . ename () + " tjänar " + emps . sal ()); } emps . stäng (); stmt . stäng ();

Separat bör det noteras att efter att ha bindit frågeresultatet till iteratorn finns det inget behov av att stänga frågeresultatet separat - förprocessorn själv kommer att göra detta för programmeraren.

Den omvända processen - omvandlingen av en iterator till ett objekt ResultSetutförs med iteratorer av en speciell typ, de så kallade svagt typade iteratorerna . 

sqlj . körtid . ResultSetIterator iter ; # sql iter = { SELECT ename FROM emp }; ResultSet rs = iter . getResultSet (); while ( rs . next ()) { System . ut . println ( "anställds namn: " + rs . getString ( 1 )); } iter . stäng ();

I det här fallet bevaras också relationen mellan iteratorn och resultatet av frågan och det är iteratorn som ska stängas.

Funktioner i SQLJ

Som tidigare nämnts är det enklaste sättet att jämföra SQLJ som teknik med en liknande Java-orienterad teknologi för samma ändamål, nämligen JDBC. Situationen kompliceras av det faktum att dessa teknologier inte är parallella och inte helt utbytbara, utan ligger arkitektoniskt ovanpå varandra.

  1. En fråga med samma syfte, skriven i JDBC-anrop och i ett SQLJ-direktiv, kommer i de flesta fall att skrivas mer kompakt i programtexten i det andra fallet, vilket minskar storleken på listningen och sannolikheten för ett fel i samband med monteringen den sista frågesträngen från små fragment;
  2. Alla SQLJ-direktiv analyseras och kontrolleras av förprocessorn i kompileringsstadiet, därför upptäcks alla syntaxfel i detta skede, till skillnad från JDBC, där korrektheten av konstruktioner endast kontrolleras i termer av Java-syntax - DBMS är redan ansvarigt för att analysera och korrigera själva frågan, vilket naturligtvis leder till att fel av detta slag kommer att upptäckas redan vid lanseringsstadiet;
  3. Själva förprocessorn (vanligen kallad sqlj) är inte en del av JDK ; det och de bibliotek som krävs för dess drift tillhandahålls vanligtvis av DBMS-leverantören. Detta är naturligt - som visas ovan är SQLJ mycket närmare DBMS än själva Java-språket; dessutom måste förprocessorn ta hänsyn till särdragen hos SQL-syntaxen för "dess" DBMS;
  4. I de flesta fall, särskilt för ofta körda komplexa frågor som fungerar med stora mängder data, kommer ett SQLJ-direktiv att köras i genomsnitt snabbare än en liknande uppsättning JDBC-anrop. Detta beror på det faktum att planen för motsvarande fråga i fallet med SQLJ-direktivet kommer att byggas endast en gång och sedan återanvändas, till skillnad från JDBC, där planen kommer att byggas på varje anrop;
  5. Frågeplanen som skapades under översättningen av SQLJ-direktivet kan konfigureras av användaren vid behov; i fallet med JDBC är detta av uppenbara skäl inte möjligt;
  6. Om frågan kräver betydande förändringar i varje enskilt fall (ett enkelt exempel: en sökfråga på en uppsättning fält, av vilka några kan sakna värden), är det lättare att använda JDBC, eftersom det inte finns några fördelar med att använda SQLJ;
  7. Eftersom när du använder JDBC finns det inget behov av ett extra steg av kodbehandling - översättning, kommer kompileringsprocessen i det här fallet att vara snabbare.

Nackdelar med SQLJ

  1. SQLJ kräver ytterligare ett förbearbetningssteg.
  2. De flesta IDE:er har inte SQLJ-stöd.
  3. SQLJ har inte stöd i de flesta ORM-ramverk som Hibernate.

Programvarustöd

Oracle

DB/2

Informix

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

se inbyggd SQLJ användarhandbok

Länkar

  1. Andrew Eisenberg, Jim Melton. Bindningar för objektspråk (död länk) . Hämtad 12 november 2008. Arkiverad från originalet 17 september 2011. 
  2. Andrew Eisenberg, Jim Melton. SQLJ - Del 1 (inte tillgänglig länk) . Hämtad 12 november 2008. Arkiverad från originalet 13 februari 2009. 
  3. IBM Redbooks. DB2 för z/OS och OS/390: Klar för Java (länk ej tillgänglig) . Hämtad 12 november 2008. Arkiverad från originalet 25 augusti 2011. 
  4. Oracle Database 11g. SQLJ Developer's Guide and Reference (inte tillgänglig länk) . Hämtad 12 november 2008. Arkiverad från originalet 25 augusti 2011.