SQLJ

Материал из Seo Wiki - Поисковая Оптимизация и Программирование
Перейти к навигацииПерейти к поиску

SQLJ — подмножество стандарта SQL, направленное на объединение преимуществ синтаксиса языков SQL и Java ради удобства реализации бизнес-логики и работы с данными. Данный стандарт разработан консорциумом, состоящим из компаний IBM, Micro Focus, Microsoft, Compaq (точнее, его подразделение, занимающееся СУБД, которое, скорее, можно отнести к приобретенной компании Tandem), Informix, Oracle, Sun и Sybase.

Предыстория

На момент появления консорциума JSQL (впоследствии ставшего одноимённым с разрабатываемым им стандартом) в 1997 году идея о взаимодействии реляционных СУБД и программ на Java была не нова. Компанией JavaSoft (дочерним подразделением компании Sun) уже был разработан интерфейс JDBC (англ. Java DataBase Connectivity — «соединение с БД средствами Java»), включённый в стандарт языка, начиная с момента выпуска JDK 1.1. Однако в силу определённых причин (см. «SQLJ и JDBC») возможностей, предоставляемых этим интерфейсом было недостаточно.

Спецификация стандарта SQLJ состоит из трех частей:

  • Уровень 0 регламентирует встраивание SQL-операторов в текст программы на Java;
  • Уровень 1 определяет обратное включение, а именно, реализацию в использующих SQL СУБД хранимых процедур и функций на языке Java;
  • Уровень 2 устанавливает соответствие между типами данных.

К концу 1998 года все три уровня спецификации были завершены и представлены для рассмотрения в ANSI в качестве дополнений к стандарту SQL. Первые две части нового стандарта были включены соответственно в части SQL/OLB и SQL/PSM стандарта SQL:1999; третья часть вошла как отдельный модуль SQL/JRT в стандарт SQL:2003

Обычно применительно к разработке приложений, работающих с БД, под SQLJ обычно понимается именно уровень 0.

Пример кода

Приведем простой пример Java-класса, использующего SQLJ для получения результатов запроса из Oracle. <source lang="java"> 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) throws SQLException {
     String fullname = null;
     String street = null;
     #sql {
        SELECT fullname,
           street INTO :OUT fullname,
           :OUT street FROM customer WHERE ID = :IN id};
     System.out.println("Customer with ID = " + id);
     System.out.println();
     System.out.println(fullname + " " + street);
  }

}</source> Из рассмотрения приведённого кода ясно, что в сам текст процедуры singleRowQuery встраивается SQL-запрос, и встраивание это организовано по определённым правилам:

  • Текст запроса находится внутри директивы #sql {...};
  • Переменные, внешние по отношению к SQL-запросу, задаются внутри него в определенном формате

Подробно все синтаксические конструкции будут рассмотрены далее.

SQLJ и JDBC

Логично возникновение вопроса о причинах создания двух параллельных стандартов для реализации технологий доступа к СУБД.

Для начала стоит отметить, что SQLJ и JDBC относятся к разным семействам стандартов и концептуально они разные. JDBC является API, входящим в стандарт языка Java и ориентированным на передачу сформированной программой SQL-конструкции в БД, а также обработку результата. SQLJ же является подмножеством стандарта SQL SQL/OLB - для него первичным является понятие базы данных, а язык, в который включаются SQL-конструкции, вторичен. Согласно этому стандарту встраивание SQL-операторов допускается не только в Java, но и в языки программирования Ada, C, COBOL, Fortran, MUMPS, PL/I.

Далее, использование SQLJ на самом деле неявно подразумевает вызов JDBC-методов, так как в данном случае они выполняют роль соответственно высоко- и низкоуровневого API. Если углубиться в подробности реализации технологий SQLJ и JDBC, то можно обнаружить, что любые SQLJ-директивы прозрачно для программиста специальной подсистемой, называемой SQLJ-препроцессором, транслируются в JDBC-вызовы. Благодаря этому можно спокойно сочетать в одном фрагменте кода SQLJ- и JDBC-вызовы, при необходимости используя общий контекст.

На самом деле, в каждом конкретном случае, когда требуется выполнение SQL-оператора, выбор между SQLJ и JDBC стоит делать, исходя из характера предполагаемой операции. Если это сложный поисковый запрос с возможными вариациями по количеству условий на поиск - тогда однозначно более целесообразно будет формирование текстовой строки запроса и последующее его выполнение через JDBC; если же требуется просто подстановка каких-то переменных либо вычислимых выражений — тогда эргономичнее в части длины кода будет написать SQLJ-директиву.

Синтаксис

Для того, чтобы эффективно использовать синтаксические новшества, вносимые стандартом SQLJ, необходимо предварительно разобраться в их особенностях, связанных с процессом разбора SQLJ-конструкций.

Любые SQLJ-конструкции начинаются с директивы #sql, в частности, блоки, содержащие внутри себя собственно SQL-запросы, задаются как #sql {…}.

Внешние переменные

В терминологии SQLJ внешней переменной (англ. host variable) называется переменная SQLJ-конструкции, используемая для получения значений или передачи их во внешнюю относительно конструкции программную среду. К примеру: <source lang="java"> int i, j; i = 1;

  1. sql { SELECT field INTO :OUT j
           FROM table
           WHERE id = :IN i };

System.out.println(j); </source> Внешние переменные для избежания неоднозначностей должны задаваться в определённом виде, а именно:

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

Модификаторы IN, OUT, INOUT опциональны и используются для указания переменных, соответственно, передающих значение извне в SQLJ-конструкцию; возвращающих значение вовне и выполняющих обе функции. Данные ключевые слова используются не только для этого — также они задают метод доступа к внешним переменным внутри SQLJ-конструкции: при наличии модификатора IN возможно только чтение значения переменной, при наличии OUT — только запись, при наличии INOUT — полный доступ. По умолчанию (при отсутствии явно заданного модификатора) переменные объявляются с неявным модификатором INOUT.

Внешние выражения

Вместо просто переменных в SQLJ-конструкциях можно использовать выражения, содержащие внешние переменные, чаще называемые просто внешними выражениями (англ. host expressions). Они имеют определённый синтаксис:

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

Основной нюанс при использовании внешних выражений заключается в том, что их использование может повлечь за собой определённые последствия, связанные с тем, что разбор SQLJ-конструкции препроцессором при наличии нескольких внешних выражений идёт в определённом порядке, а при использовании в выражениях присваиваний результат присваивания может быть передан в программную среду.

Для иллюстрации данных двух моментов разберем простой пример использования внешних выражений: <source lang="java"> int i = 1;

  1. sql { SELECT result

FROM table1 WHERE field1 = :(x[i++]) AND field2 = :(y[i++]) AND field3 = :(z[i++]) }; System.out.println(i);</source>

Исходя из опыта программирования, можно попытаться предположить, что

  1. Значение переменной i в процессе разбора SQL-выражения не будет изменяться;
  2. Сформированный запрос будет иметь вид

<source lang="sql"> SELECT result

   FROM    table1
   WHERE   field1 = :(x[1]) AND field2 = :(y[1]) AND field3 = :(z[1])

</source> Однако и первое, и второе утверждения — неверны. Для проверки этого составим простую схему, проясняющую порядок разбора данной конструкции SQLJ-препроцессором:

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

Следовательно:

  1. После выполнения SQLJ-директивы будет иметь место i = 4;
  2. Выполняться будет запрос

<source lang="sql"> SELECT result

   FROM    table1
   WHERE   field1 = :(x[1]) AND field2 = :(y[2]) AND field3 = :(z[3])

</source>

Контексты

В терминологии SQLJ и JDBC контекстом подключения называется совокупность из трёх параметров, однозначно ими определяемая:

  1. название базы данных;
  2. идентификатор сессии;
  3. идентификатор активной транзакции.

Для любой SQLJ-конструкции контекст, в котором она будет исполняться, можно определить явно: #sql [<контекст>] {…}.

В рамках директивы #sql можно также создавать новые контексты для последующего использования: #sql context <контекст>. Если контекст явно не задан, то конструкция считается выполняемом в контексте по умолчанию (англ. default context). При необходимости контекст по умолчанию может быть изменён.

Итераторы

Итератором в терминологии стандарта SQLJ называется объект для хранения результата запроса, возвращающего более одной записи. По своей сути и реализации он представляет собой не просто множество записей, а множество с некоторым упорядочением на нем, позволяющим использовать полученные записи последовательно. В этом плане итератор имеет много общего с курсором.

Стандартом предусмотрены два типа итераторов — разница между ними достаточно интересна: итераторы с привязкой по позиции в использовании требуют более SQL-подобного синтаксиса, в отличие от итераторов с привязкой по столбцам, которые очень близки по использованию к объектам.

Итераторы с привязкой по позиции

Первым типом итератора является итератор с привязкой по позициям. Он объявляется следующим образом: #sql public iterator ByPos (String, int). Ясно видно, что в данном случае привязка результатов запроса к итератору осуществляется просто по совпадению типов данных между итератором и результатом запроса. Однако для этого требуется, чтобы типы данных у итератора и результата запроса могли быть отображены друг на друга согласно стандарту SQL/JRT.

Создадим простую таблицу: <source lang="sql"> CREATE TABLE people ( fullname VARCHAR(50), birthyear NUMERIC(4,0)) </source> Теперь с помощью итератора первого типа и конструкции FETCH … INTO … произведем выборку данных из результата запроса: <source lang="java">ByPos positer; String name = null; int year = 0;

  1. sql positer = {SELECT fullname, birthyear FROM people};

for(;;) { #sql {FETCH :positer INTO :name, :year}; if (positer.endFetch()) break; System.out.println(name + " was born in " + year);

}</source> Первой директивой осуществляется привязка результата запроса к итератору; второй с помощью конструкции FETCH … INTO … из результата последовательно считывается по одной записи.

Итераторы с именованием столбцов

Вторым типом итератора, более приближенного по использованию к обычным объектам, является итератор с именованием столбцов. Для указанной таблицы создание итератора второго типа будет выглядеть следующим образом: <source lang="java">

  1. sql public iterator ByName (

String fullNAME, int birthYEAR);</source> Используется он как обычный объект, а именно, доступ к полям осуществляется через соответствующие акцессорные методы: <source lang="java"> ByName namiter;

  1. sql namiter = {SELECT fullname, birthyear FROM people};

String s; int i; while (namiter.next()) { i = namiter.birthYEAR(); s = namiter.fullNAME(); System.out.println(s + " was born in "+i); } </source> Однако существует правило, которое должно быть соблюдено — имена полей итератора должны совпадать (без учёта регистра) с именами полей в запросе. Это связано с процессом разбора SQLJ-конструкции препроцессором. В случае, если имя столбца в БД имеет название, несовместимое с правилами именования переменных в Java, необходимо использовать в запросе, формирующем итератор, псевдонимы.

Вызовы процедур и функций

Вызовы процедур очень просто записываются с использованием внешних переменных <source lang="java">#sql {CALL proc (:myarg)};</source> Функции, в свою очередь, вызываются с использованием конструкции VALUE <source lang="java"> int i;

  1. sql i = {VALUES(func(34))};</source>

Взаимодействие с JDBC

Так как SQLJ-директивы при своём использовании используют JDBC-вызовы, то представляет интерес возможность использовать эти технологии совместно. Достаточно легко преобразовывать итераторы в объекты ResultSet и наоборот.

Преобразование объекта ResultSet осуществляется очень просто. Для этого сначала нужно определить итератор с именованием столбцов (в нашем примере он будет обозначаться Employees, а затем выполнить операцию CAST: <source lang="java">

  1. sql iterator Employees (String ename, double sal);

PreparedStatement stmt = conn.prepareStatement(); String query = "SELECT ename, sal FROM emp WHERE "; query += whereClause; ResultSet rs = stmt.executeQuery(query); Employees emps;

  1. sql emps = {CAST :rs};

while (emps.next()) {

     System.out.println(emps.ename() + " earns " + emps.sal());

} emps.close(); stmt.close(); </source> Отдельно стоит обратить внимание, что после привязки результата запроса к итератору отдельно закрывать результат запроса нет надобности — это за программиста сделает сам препроцессор.

Обратный процесс — преобразование итератора в объект ResultSet производится с помощью итераторов особого типа, так называемых слабо типизированных (англ. weakly typed) итераторов. <source lang="java"> sqlj.runtime.ResultSetIterator iter;

  1. sql iter = {SELECT ename FROM emp};

ResultSet rs = iter.getResultSet(); while (rs.next()) {

     System.out.println("employee name: " + rs.getString(1));

} iter.close(); </source> В этом случае связь между итератором и результатом запроса также сохраняется и закрывать следует именно итератор.

Плюсы и минусы SQLJ

Как уже упоминалось ранее, сравнивать SQLJ как технологию проще всего с аналогичной Java-ориентированной технологией того же назначения, а именно — с JDBC. Ситуация усложняется тем, что эти технологии не параллельны и не вполне взаимозаменяемы, а находятся друг над другом архитектурно.

  1. Запрос одинакового назначения, записанный в JDBC-вызовах и в SQLJ-директиве, в большинстве случаев будет более компактно записан в тексте программы именно во втором случае, что уменьшает размер листинга и вероятность ошибки, связанной со сборкой итоговой строки запроса из небольших фрагментов;
  2. Любая SQLJ-директива на этапе компиляции разбирается и проверяется препроцессором, следовательно, все ошибки синтаксиса выявляются ещё на этом этапе, в отличие от JDBC, где контролируется правильность конструкций только с точки зрения синтаксиса Java — за разбор и правильность собственно запроса отвечает уже СУБД, что, естественно, приводит к тому, что ошибки такого рода будут выявлены уже на этапе запуска;
  3. Собственно сам препроцессор (обычно имеющий название sqlj) не входит в JDK; он и необходимые для его работы библиотеки обычно предоставляются производителем СУБД. Это закономерно — как показано выше, SQLJ гораздо более близок к СУБД, чем собственно к языку Java; более того, препроцессор должен учитывать особенности SQL-синтаксиса «своей» СУБД;
  4. В большинстве случаев — особенно это касается часто выполняющихся сложных запросов, работающих с большими массивами данных, — SQLJ-директива будет выполняться в среднем быстрее аналогичного набора JDBC-вызовов. Это связано с тем, что план для соответствующего запроса в случае SQLJ-директивы будет строиться только один раз, а затем использоваться повторно, в отличие от JDBC, где построение плана будет осуществляться при каждом вызове;
  5. Создаваемый при трансляции SQLJ-директивы план запроса при необходимости может быть подвергнут настройке со стороны пользователя; в случае JDBC такая возможность по понятным причинам отсутствует;
  6. Если запрос требует значительных изменений в каждом конкретном случае (простой пример: поисковый запрос по набору полей, значения в части которых могут отсутствовать), то проще использовать JDBC, так как преимуществ в использовании SQLJ здесь нет;
  7. Так как при использовании JDBC нет надобности в дополнительном этапе обработки кода — трансляции, то процесс компиляции в этом случае будет быстрее.

Поддержка программными средствами

Oracle

DB/2

Informix

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

см. Embedded SQLJ User's Guide

Ссылки

  1. Эндрю Эйзенберг, Джим Мелтон Связывания для объектных языков. Проверено 12 ноября 2008.
  2. Эндрю Эйзенберг, Джим Мелтон SQLJ – Часть 1. Проверено 12 ноября 2008.
  3. IBM Redbooks DB2 for z/OS and OS/390: Ready for Java. Проверено 12 ноября 2008.
  4. Oracle Database 11g SQLJ Developer's Guide and Reference. Проверено 12 ноября 2008.

de:SQLJ en:SQLJ es:SQLJ it:SQLJ ja:SQLJ

Если вам нравится SbUP.com Сайт, вы можете поддержать его - BTC: bc1qppjcl3c2cyjazy6lepmrv3fh6ke9mxs7zpfky0 , TRC20 и ещё....