SQLJSQLJ — підмножина стандарту 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 складається з трьох частин:
До кінця 1998 року всі три рівні специфікації були завершені й подані до розгляду в ANSI як доповнення до стандарту SQL. Перші дві частини нового стандарту були включені відповідно в частини SQL/OLB й SQL/PSM стандарту SQL:1999; третя частина увійшла як окремий модуль SQL/JRT до стандарту SQL:2003 Зазвичай у застосуванні до розробки додатків, що працюють з БД, під SQLJ здебільшого розуміють саме рівень 0. Приклад кодуНаведемо простий приклад Java-класу, що використовує SQLJ для отримання результатів запиту з Oracle. 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);
}
}
З розгляду наведеного коду зрозуміло, що в сам текст процедури
Детально всі синтаксичні конструкції буде розглянуто далі. SQLJ і JDBCВ чому ж причини створення двох паралельних стандартів для реалізації технологій доступу до СУБД? Для початку слід зазначити, що SQLJ і JDBC належать до різних родин стандартів і концептуально вони різні. JDBC є API, що входить у стандарт мови Java й орієнтований на передачу сформованої програмою SQL-конструкції в БД, а також обробку результату. SQLJ же є підмножиною стандарту SQL SQL/OLB — для нього первинним є поняття бази даних, а мова, в яку включаються SQL-конструкції, вторинний. Згідно з цим стандартом вбудовування SQL-операторів допускається не лише в Java, але й у мови програмування Ada, C, COBOL, Fortran, MUMPS, PL/1. Далі, використання SQLJ насправді неявно означає виклик JDBC-методів, оскільки в даному випадку вони виконують роль відповідно високо- й низькорівневого API. Якщо заглибитися в подробиці реалізації технологій SQLJ і JDBC, то можна виявити, що будь-які SQLJ-директиви прозоро для програміста спеціальною підсистемою (SQLJ-препроцесором) транслюються в JDBC-виклики. Завдяки цьому можна спокійно поєднувати в одному фрагменті коду SQLJ- і JDBC-виклики, за необхідності використовуючи спільний контекст. Насправді в кожному конкретному випадку, коли потрібне виконання SQL-оператора, вибір між SQLJ і JDBC слід робити, виходячи з характеру очікуваної операції. Якщо це складний пошуковий запит з можливими варіаціями за кількістю умов на пошук — тоді однозначно доцільніше формування текстового рядка запиту й подальше його виконання через JDBC; якщо ж потрібна просто підстановка якихось змінних або обчислюваних виразів — тоді ергономічніше з точки зору довжини коду буде написати SQLJ-директиву. СинтаксисДля того, щоб ефективно використовувати синтаксичні нововведення, внесені стандартом SQLJ, необхідно попередньо розібратися в їх особливостях, пов'язаних з процесом розбору SQLJ-конструкцій. Будь-які SQLJ-конструкції починаються з директиви Зовнішні змінніВ термінології SQLJ зовнішньою змінною (англ. host variable) називається змінна SQLJ-конструкції, використовувана для отримання значень чи передачі їх у зовнішнє відносно конструкції програмне середовище. Наприклад: int i, j;
i = 1;
#sql { SELECT field INTO :OUT j
FROM table
WHERE id = :IN i };
System.out.println(j);
Зовнішні змінні для уникнення неоднозначностей мають задаватися в певному вигляді, а саме:
Модифікатори Зовнішні виразиЗамість просто змінних в SQLJ-конструкціях можна використовувати вирази, що містять зовнішні змінні, що частіше називаються просто зовнішніми виразами (англ. host expressions). Вони мають певний синтаксис:
Основний нюанс при використанні зовнішніх виразів полягає в тому, що їх використання може потягнути за собою певні наслідки, пов'язані з тим, що розбір SQLJ-конструкції препроцесором за наявності кількох зовнішніх виразів іде в визначеному порядку, а при використанні в виразах присвоювань результат присвоєння може передаватися в програмне середовище. Для ілюстрації цих двох моментів розберімо простий приклад використання зовнішніх виразів: int i = 1;
#sql { SELECT result
FROM table1
WHERE field1 = :(x[i++]) AND field2 = :(y[i++]) AND field3 = :(z[i++]) };
System.out.println(i);
Виходячи з досвіду програмування, можна зробити припущення, що
SELECT result
FROM table1
WHERE field1 = :(x[1]) AND field2 = :(y[1]) AND field3 = :(z[1])
Однак і перше, і друге твердження — хибні. Для перевірки цього складімо просту схему, що прояснить порядок розбору даної конструкції SQLJ-препроцесором:
Отже:
SELECT result
FROM table1
WHERE field1 = :(x[1]) AND field2 = :(y[2]) AND field3 = :(z[3])
КонтекстиВ термінології SQLJ і JDBC контекстом підключення називається сукупність із трьох параметрів, що однозначно ними визначається:
Для будь-якої SQLJ-конструкції контекст, у якому вона буде виконуватись, можна визначити явно: В рамках директиви ІтераториІтератором в термінології стандарту SQLJ називається об'єкт для зберігання результату запиту, що повертає більше одного запису. За своєю суттю й реалізацією він являє собою не просто множину записів, а множину деяким упорядкуванням у ній, що дозволяє обробляти отримані записи послідовно. В цьому плані ітератор має багато спільного з курсором. Стандартом передбачені два типи ітераторів — різниця між ними достатньо цікава: ітератори з прив'язкою за позицією — вимагають більш SQL-подібного синтаксису, на відміну від ітераторів з прив'язкою за стовпчиками, котрі дуже близькі за способом використання до об'єктів. Ітератори з прив'язкою за позицієюПершим типом ітератора є ітератор з прив'язкою за позиціями. Він оголошується так: Створімо просту таблицю: CREATE TABLE people (
fullname VARCHAR(50),
birthyear NUMERIC(4,0))
Тепер з допомогою ітератора першого типу й конструкції ByPos positer;
String name = null;
int year = 0;
#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);
}
Першою директивою здійснюється прив'язка результату запиту до ітератора; другою з допомогою конструкції Ітератори з іменуванням стовпчиківДругим типом ітератора, більш наближеного за використанням до звичайних об'єктів, є ітератор з іменуванням стовпчиків. Для вказаної таблиці створення ітератора другого типу буде виглядати так: #sql public iterator ByName (
String fullNAME,
int birthYEAR);
Використовується він як звичайний об'єкт, а саме, доступ до полів здійснюється через відповідні акцесорні методи: ByName namiter;
#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);
}
Однак існує правило, яке має виконуватись — імена полів ітератора мають збігатися (без урахування регістру) з іменами полів у запиті. Це пов'язано з процесом розбору SQLJ-конструкції препроцесором. У випадку, якщо ім'я стовпчика в БД має назву, несумісну з правилами іменування змінних у Java, необхідно використовувати псевдоніми в запиті, що формує ітератор. Виклики процедур і функційВиклики процедур дуже просто записуються з використанням зовнішніх змінних: #sql {CALL proc (:myarg)};
Функції, в свою чергу, викликаються з використанням конструкції int i;
#sql i = {VALUES(func(34))};
Взаємодія з JDBCОскільки SQLJ-директиви при своєму використанні здійснюють JDBC-виклики, то цікавою є можливість використати ці технології разом. Досить легко перетворити ітератори в об'єкти Перетворення об'єкта #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;
#sql emps = {CAST :rs};
while (emps.next()) {
System.out.println(emps.ename() + " earns " + emps.sal());
}
emps.close();
stmt.close();
Окремо варто зазначити, що після прив'язки результату запиту до ітератора окремо закривати результат запиту непотрібно — це за програміста зробить сам препроцесор. Зворотний процес — перетворення ітератора в об'ект sqlj.runtime.ResultSetIterator iter;
#sql iter = {SELECT ename FROM emp};
ResultSet rs = iter.getResultSet();
while (rs.next()) {
System.out.println("employee name: " + rs.getString(1));
}
iter.close();
В цьому випадку зв'язок між ітератором і результатом запиту також зберігається й закривати слід саме ітератор. Плюси й мінуси SQLJЯк уже згадувалось раніше, порівнювати SQLJ як технологію простіше всього з аналогічною Java-орієнтованою технологією того ж призначення, а саме — з JDBC. Ситуація ускладнюється тим, що ці технології не паралельні й не повністю взаємозамінні, а перебувають одна над одною архітектурно.
ПрикладиВ наступних прикладах порівнюється синтаксис SQLJ з використанням JDBC.
Підтримка програмними засобамиOracleDB/2Informixhttp://www-01.ibm.com/software/data/informix/pubs/library/iif.html [Архівовано 12 лютого 2009 у Wayback Machine.] Див. Embedded SQLJ User’s Guide Посилання
Зовнішні посилання
|