SQLJ

SQLJ  es un subconjunto del estándar SQL , destinado a combinar las ventajas de la sintaxis SQL y Java para la conveniencia de implementar la lógica comercial y trabajar con datos. Este estándar fue desarrollado por un consorcio formado por IBM , Micro Focus , Microsoft , Compaq (más precisamente, su división DBMS, que más bien puede atribuirse a la empresa adquirida Tandem ), Informix , Oracle , Sun y Sybase .

Antecedentes

En el momento de la aparición del consorcio JSQL (que luego tomó el mismo nombre que el estándar que desarrolló) en 1997, la idea de interacción entre SGBD relacionales y programas Java no era nueva. JavaSoft ( una subsidiaria de Sun) ya ha desarrollado la interfaz JDBC ( Java DataBase Connectivity )   incluida en el lenguaje estándar desde el lanzamiento de JDK 1.1. Sin embargo, por ciertas razones (ver SQLJ y JDBC ), las características proporcionadas por esta interfaz no fueron suficientes.

La especificación estándar SQLJ consta de tres partes:

A fines de 1998, los tres niveles de la especificación se completaron y enviaron a ANSI para su consideración como adiciones al estándar SQL. Las dos primeras partes del nuevo estándar se incluyeron respectivamente en las partes SQL/OLB y SQL/PSM del estándar SQL:1999 ; la tercera parte se incluyó como un módulo SQL/JRT separado en el estándar SQL:2003

Habitualmente, en relación al desarrollo de aplicaciones que trabajan con la base de datos, se suele entender SQLJ como nivel 0.

Código de ejemplo

Aquí hay un ejemplo simple de una clase de Java que usa SQLJ para obtener resultados de consultas de Oracle .

importar java.sql.* ; importar oracle.sqlj.runtime.Oracle ; Clase pública SingleRowQuery extiende Base { public static void main ( String [] args ) { try { connect (); solaFilaConsulta ( 1 ); } captura ( SQLException e ) { e . imprimirStackTrace (); } } public static void singleRowQuery ( int id ) lanza SQLException { String fullname = null ; Cadena calle = nulo ; # sql { SELECCIONE nombre completo , calle ENTRADA : SALIDA nombre completo , : SALIDA calle DESDE cliente DONDE ID = : ENTRADA id }; sistema _ fuera _ println ( "Cliente con ID = " + id ); sistema _ fuera _ imprimirln (); sistema _ fuera _ println ( nombre completo + " " + calle ); } }

Del código anterior, está claro que una singleRowQueryconsulta SQL está incrustada en el texto del procedimiento mismo, y esta incrustación está organizada de acuerdo con ciertas reglas:

  • El texto de la solicitud está dentro de la directiva #sql {...};
  • Las variables externas a la consulta SQL se establecen dentro de ella en un formato específico

Todas las construcciones sintácticas se discutirán en detalle a continuación.

SQLJ y JDBC

Es lógico que surja la pregunta sobre las razones para crear dos estándares paralelos para implementar tecnologías de acceso DBMS.

Para empezar, vale la pena señalar que SQLJ y JDBC pertenecen a diferentes familias de estándares y son conceptualmente diferentes. JDBC es una API que forma parte del estándar del lenguaje Java y está enfocada en transferir la construcción SQL generada por el programa a la base de datos, así como procesar el resultado. SQLJ es un subconjunto del estándar SQL SQL/OLB  ; para él, el concepto de una base de datos es principal y el lenguaje en el que se incluyen las construcciones SQL es secundario. De acuerdo con este estándar, se permite incrustar declaraciones SQL no solo en Java, sino también en los lenguajes de programación Ada , C , COBOL , Fortran , MUMPS , PL/I .

Además, el uso de SQLJ en realidad implícitamente implica llamar a métodos JDBC, ya que en este caso actúan como una API de alto y bajo nivel, respectivamente . Si profundiza en los detalles de la implementación de las tecnologías SQLJ y JDBC, puede encontrar que cualquier directiva SQLJ se traduce en llamadas JDBC de forma transparente para el programador mediante un subsistema especial llamado preprocesador SQLJ . Esto le permite combinar de forma segura llamadas SQLJ y JDBC en el mismo fragmento de código, utilizando un contexto común si es necesario.

De hecho, en cualquier caso particular en el que se deba ejecutar una declaración SQL, la elección entre SQLJ y JDBC debe hacerse en función de la naturaleza de la operación prevista. Si se trata de una consulta de búsqueda compleja con posibles variaciones en el número de condiciones de búsqueda, definitivamente sería más conveniente formar una cadena de consulta de texto y luego ejecutarla a través de JDBC; si solo necesita sustituir algunas variables o expresiones computables, será más ergonómico en términos de longitud de código escribir una directiva SQLJ.

Sintaxis

Para usar de manera efectiva las innovaciones sintácticas introducidas por el estándar SQLJ, primero debe comprender sus características relacionadas con el proceso de análisis de construcciones SQLJ.

Cualquier construcción SQLJ comienza con la directiva #sql, en particular, los bloques que contienen consultas SQL se especifican como #sql {…}.

Variables externas

En la terminología de SQLJ , una variable externa ( ing.  host variable ) es una variable de construcción SQLJ que se utiliza para recibir valores o pasarlos al entorno del programa externo a la construcción. Por ejemplo:

int i , j ; yo = 1 ; # sql { SELECCIONE el campo EN : FUERA j DE la tabla DONDE id = : EN i }; sistema _ fuera _ imprimir ( j );

Para evitar ambigüedades, las variables externas deben especificarse de cierta forma, a saber:

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

Los modificadores IN, OUT, son INOUTopcionales y se utilizan para especificar variables, respectivamente, pasando un valor desde el exterior a la construcción SQLJ; devolver un valor al exterior y realizar ambas funciones. Estas palabras clave se usan no solo para esto, sino que también establecen el método de acceso a variables externas dentro de la construcción SQLJ: si hay un modificador IN, solo es posible leer el valor de la variable, si está presente OUT , solo escribir, si está presente INOUT , acceso completo . De forma predeterminada (en ausencia de un modificador especificado explícitamente), las variables se declaran con un modificador implícito INOUT.

Expresiones externas

En lugar de solo variables en construcciones SQLJ, puede usar expresiones que contienen variables externas, a menudo llamadas solo expresiones externas ( expresiones de host en inglés  ). Tienen una sintaxis específica:

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

El principal matiz cuando se usan expresiones externas es que su uso puede tener ciertas consecuencias relacionadas con el hecho de que el análisis de la construcción SQLJ por parte del preprocesador en presencia de varias expresiones externas se realiza en un cierto orden, y cuando se usa en expresiones de asignación, el resultado de la asignación se puede transferir al entorno de software.

Para ilustrar estos dos puntos, veamos un ejemplo simple del uso de expresiones externas:

int i = 1 ; # sql { SELECCIONE el resultado DE la tabla1 DONDE campo1 = :( x [ i ++] ) Y campo2 = :( y [ i ++] ) Y campo3 = :( z [ i ++] ) }; sistema _ fuera _ println ( i );

Con base en la experiencia de programación, uno puede tratar de asumir que

  1. El valor de la variable ino cambiará durante el análisis de la instrucción SQL;
  2. La consulta generada se verá como
SELECCIONE el resultado DE la tabla1 DONDE campo1 = :( x [ 1 ]) Y campo2 = :( y [ 1 ]) Y campo3 = :( z [ 1 ])

Sin embargo, tanto la primera como la segunda afirmación son falsas. Para verificar esto, hagamos un diagrama simple que aclare el orden de análisis de esta construcción por parte del preprocesador SQLJ:

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

Como consecuencia:

  1. Después de ejecutar la directiva SQLJ, aparecerá un i = 4;
  2. La solicitud será ejecutada
SELECCIONE el resultado DE la tabla1 DONDE campo1 = :( x [ 1 ]) Y campo2 = :( y [ 2 ]) Y campo3 = :( z [ 3 ])

Contextos

En la terminología de SQLJ y JDBC, un contexto de conexión es un conjunto de tres parámetros definidos de forma exclusiva por ellos:

  1. nombre de la base de datos;
  2. identificador de sesión;
  3. ID de la transacción activa.

Para cualquier construcción SQLJ, el contexto en el que se ejecutará se puede definir explícitamente: #sql [<контекст>] {…}.

Dentro de una directiva #sql, también puede crear nuevos contextos para su uso posterior: #sql context <контекст>. Si el contexto no se establece explícitamente, se considera que la construcción se ejecuta en el contexto predeterminado .  Si es necesario, se puede cambiar el contexto predeterminado.

Iteradores

Un iterador en la terminología del estándar SQLJ es un objeto para almacenar el resultado de una consulta que devuelve más de un registro. En su esencia e implementación, no es solo un conjunto de registros, sino un conjunto con cierto ordenamiento, que permite utilizar secuencialmente los registros recibidos. En este sentido, un iterador tiene mucho en común con un cursor .

El estándar proporciona dos tipos de iteradores: la diferencia entre ellos es bastante interesante: los iteradores vinculados a la posición requieren una sintaxis más similar a SQL en uso, a diferencia de los iteradores vinculados a columnas, que se usan muy cerca de los objetos.

Iteradores ligados a la posición

El primer tipo de iterador es el iterador ligado a la posición. Se declara de la siguiente manera: #sql public iterator ByPos (String, int). Está claro que en este caso, la vinculación de los resultados de la consulta a un iterador se lleva a cabo simplemente haciendo coincidir los tipos de datos entre el iterador y el resultado de la consulta. Sin embargo, esto requiere que los tipos de datos del iterador y el resultado de la consulta se puedan asignar entre sí de acuerdo con el estándar SQL/JRT.

Vamos a crear una tabla simple:

CREAR TABLA personas ( nombre completo VARCHAR ( 50 ), año de nacimiento NUMÉRICO ( 4 , 0 ))

Ahora, usando el iterador del primer tipo y la construcción , buscaremos FETCH … INTO …datos del resultado de la consulta:

posicionador de bypos ; Cadena nombre = nulo ; int año = 0 ; # sql positer = { SELECT fullname , birthyear FROM people }; for (;;) { # sql { FETCH : postular INTO : nombre , : año }; if ( poster . endFetch ()) break ; sistema _ fuera _ println ( nombre + "nació en" + año ); }

La primera directiva vincula el resultado de la consulta a un iterador; el segundo, usando una construcción FETCH … INTO …, un registro a la vez se lee secuencialmente del resultado.

Iteradores con nombres de columna

El segundo tipo de iterador, más parecido a los objetos normales, es el iterador con nombre de columna. Para la tabla especificada, la creación de un iterador del segundo tipo se verá así:

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

Se utiliza como un objeto regular, es decir, el acceso a los campos se realiza a través de los métodos de acceso correspondientes:

PorNombre nombre ; # sql namiter = { SELECT fullname , birthyear FROM people }; Cadena s ; ent i ; while ( nombre . siguiente ()) { i = nombre . AÑONACIMIENTO (); s = nombre . nombre completo (); sistema _ fuera _ println ( s + " nació en " + i ); }

Sin embargo, hay una regla que se debe observar: los nombres de los campos del iterador deben coincidir (sin distinción entre mayúsculas y minúsculas) con los nombres de los campos en la consulta . Esto se debe al proceso de análisis de la construcción SQLJ por parte del preprocesador. Si el nombre de una columna de la base de datos tiene un nombre incompatible con las reglas de nomenclatura de variables en Java, debe utilizar alias en la consulta que forma el iterador.

Llamadas a procedimientos y funciones

Las llamadas a procedimientos son muy fáciles de escribir usando variables externas.

# sql { LLAMADA proc (: myarg )};

Las funciones, a su vez, se llaman usando la construcciónVALUE

ent i ; # sql i = { VALORES ( func ( 34 ))};

Interacción con JDBC

Dado que las directivas SQLJ usan llamadas JDBC cuando se usan, es interesante poder usar estas tecnologías juntas. Es bastante fácil convertir iteradores en objetos ResultSety viceversa.

Transformar un objeto ResultSetes muy fácil. Para hacer esto, primero debe definir un iterador con los nombres de las columnas (en nuestro ejemplo, se denotará Employeespor ), y luego realizar la operación CAST:

# sql iterator Empleados ( String ename , double sal ); Sentencia Preparada stmt = conn . prepararDeclaración (); String query = "SELECCIONE nombre, sal DESDE emp DONDE" ; consulta += cláusuladonde ; Conjunto de resultados rs = sentencia . ejecutar consulta ( consulta ); Empleados emps ; # sql emps = { CAST : rs }; while ( emps . next ()) { System . fuera _ println ( emps . ename () + " gana " + emps . sal ()); } emp . cerrar (); sentencia _ cerrar ();

Por separado, debe tenerse en cuenta que después de vincular el resultado de la consulta al iterador, no es necesario cerrar el resultado de la consulta por separado; el preprocesador lo hará por el programador.

El proceso inverso: la conversión de un iterador en un objeto se realiza utilizando iteradores ResultSetde un tipo especial, los llamados iteradores de tipo débil . 

sqlj . tiempo de ejecución ResultSetIterator iter ; # sql iter = { SELECCIONE nombre DESDE emp }; Conjunto de resultados rs = iter . obtener conjunto de resultados (); while ( rs . siguiente ()) { System . fuera _ println ( "nombre del empleado:" + rs . getString ( 1 )); } iter . cerrar ();

En este caso, la relación entre el iterador y el resultado de la consulta también se conserva y es el iterador el que debe cerrarse.

Características de SQLJ

Como se mencionó anteriormente, la forma más fácil de comparar SQLJ como tecnología es con una tecnología similar orientada a Java para el mismo propósito, a saber, JDBC. La situación se complica por el hecho de que estas tecnologías no son paralelas ni completamente intercambiables, sino que están arquitectónicamente superpuestas.

  1. Una consulta con el mismo propósito, escrita en llamadas JDBC y en una directiva SQLJ, en la mayoría de los casos se escribirá de forma más compacta en el texto del programa en el segundo caso, lo que reduce el tamaño de la lista y la probabilidad de un error asociado con el ensamblaje. la cadena de consulta final de pequeños fragmentos;
  2. El preprocesador analiza y verifica cualquier directiva SQLJ en la etapa de compilación, por lo tanto, todos los errores de sintaxis se detectan en esta etapa, en contraste con JDBC, donde la corrección de las construcciones se controla solo en términos de sintaxis de Java: el DBMS ya es responsable. para analizar y corregir la consulta en sí, lo que, naturalmente, conduce al hecho de que los errores de este tipo ya se detectarán en la etapa de lanzamiento;
  3. El preprocesador en sí (generalmente llamado sqlj) no es parte del JDK ; él y las bibliotecas necesarias para su funcionamiento suelen ser proporcionados por el proveedor de DBMS. Esto es natural: como se muestra arriba, SQLJ está mucho más cerca del DBMS que del propio lenguaje Java; además, el preprocesador debe tener en cuenta las peculiaridades de la sintaxis SQL de "su" SGBD;
  4. En la mayoría de los casos, especialmente para consultas complejas ejecutadas con frecuencia que trabajan con grandes cantidades de datos, una directiva SQLJ se ejecutará en promedio más rápido que un conjunto similar de llamadas JDBC. Esto se debe a que el plan para la consulta correspondiente en el caso de la directiva SQLJ se construirá una sola vez y luego se reutilizará, a diferencia de JDBC, donde el plan se construirá en cada llamada;
  5. El plan de consulta creado durante la traducción de la directiva SQLJ puede ser configurado por el usuario si es necesario; en el caso de JDBC, por obvias razones, esto no es posible;
  6. Si la consulta requiere cambios significativos en cada caso (un ejemplo simple: una consulta de búsqueda en un conjunto de campos, algunos de los cuales pueden tener valores faltantes), entonces es más fácil usar JDBC, ya que no hay ventajas en usar SQLJ;
  7. Dado que al usar JDBC no se necesita una etapa adicional de procesamiento de código: traducción, el proceso de compilación en este caso será más rápido.

Desventajas de SQLJ

  1. SQLJ requiere un paso de preprocesamiento adicional.
  2. La mayoría de los IDE no tienen soporte para SQLJ.
  3. SQLJ no tiene soporte en la mayoría de los marcos ORM como Hibernate.

Soporte de software

Oráculo

DB/2

Informix

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

consulte la Guía del usuario de SQLJ incorporado

Enlaces

  1. Andrew Eisenberg, Jim Melton. Enlaces para lenguajes de objetos (enlace muerto) . Consultado el 12 de noviembre de 2008. Archivado desde el original el 17 de septiembre de 2011. 
  2. Andrew Eisenberg, Jim Melton. SQLJ - Parte 1 (enlace no disponible) . Consultado el 12 de noviembre de 2008. Archivado desde el original el 13 de febrero de 2009. 
  3. Libros rojos de IBM . DB2 para z/OS y ​​OS/390: Listo para Java (enlace no disponible) . Consultado el 12 de noviembre de 2008. Archivado desde el original el 25 de agosto de 2011. 
  4. Base de datos Oracle 11g. Guía y referencia del desarrollador de SQLJ (enlace no disponible) . Consultado el 12 de noviembre de 2008. Archivado desde el original el 25 de agosto de 2011.