Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:JAVA. Серверные приложения
.pdf
Часть 5. Работа с базами данных при помощи Java 261
commit()
•
getAutoCommit()
•
getMetaData()
•
getWarnings()
•
isClosed()
•
isReadOnly()
•
rollback()
•
setAutoCommit()
•
Остальные методы данного класса используются в SQLJ-приложени-
ях и при попытке доступа к ним возвращают ошибку. По умолчанию
значением метода autoCommit() является off для всех соединений.
В SQLJ-приложениях, предназначенных для работы с DB2, SQL-оператор COMMIT не работает. Для установки возможности использования
оператора COMMIT в SQLJ-приложении, обращающегося к DB2, используется метод setAutoCommit() с параметром on.
В SQLJ-коде можно использовать кооментарии обоих типов: SQL-
комментарии и Java-комментарии. Использование комментариев в
SQLJ-коде позволительно для всех конструкций, но SQL-комментарии
ставятся только после метки #sql, в то время как Java-комментарии могут указываться в любом месте программы.
/* Java комментарии */
#sql c1={—SQLкомментарии /* Java комментарии */
// Java-операторы — это тоже коментарий
SELECT name FROM table };
/* Java-комvентарии */
Правильно оформленные комментарии позволяют без участия про-
граммиста создать документацию на созданное приложение. Есть очень
полезная утилитка, поставляемая в стандартном JDK. JavaDoc предназначена для генерации сопроводительной документации на основе
комментариев, используемых при создании приложения. После создания SQLJ-приложения необходимо определить роль созданного SQLJприложения. Будет ли SQLJ-приложение самостоятельным, или же ему
необходим инициатор, как, например, апплет, от этого выбора зависит,
каким способом SQLJ-приложение будет доступно другим приложениям. Также обязательно следует учесть, каким способом осуществляется
передача пользовательских параметров соединения с базой данных посредством командной строки, при помощи использования файла свойств
илb же параметры будут передаваться посредством форм графического
интерфейса.
Глава 37. Встроенные объекты
В SQLJ-приложениях, так же как и в любом другом предназначен-
ном для взаимодействия с базой данных, при соединении с СУБД необходимо в вести параметры, идентифицирующие пользователя, сделавшего запрос, пароль, логин, а также может потребоваться ввод другой

262 Часть 5. Работа с базами данных при помощи Java
служебной информации, имя базы данных и тип драйвера. Можно каждый раз воспользоваться интерактивным SQL, но в таком случае отпадает необходимость в SQLJ-приложении. Спецификация SQLJ описывает специальные встроенные объекты, облегчающие задачи соединения
клиента с базой данных. Два класса ConnectionContext и ExecutionContext предназначены для получения информации о пользовательском соединении и пользовательском SQL-запросе. При использовании СУБД
часто встречаются задачи, выполняемые SQL-операторами, требующие
многократного повторения или повторения одних и тех же действий для
разных SQL-операторов. SQLJ-приложения содержат встроенный объект Iterator, упрощающий выполнение однородных SQL-операторов.
37.1. Context
Встроенный объект, определяющий внутренние значения работы
SQL-оператора — это context. Существуют два типа Context-объектов:
ConnectionContext и ExecutionContext. Синтаксис обоих идентичен, и
оба могут не указываться в SQLJ-приложении, поскольку значения
присваиваются в момент трансляции SQLJ-приложения, а значит, устанавливаются по умолчанию. Если трансляция SQLJ-приложения выполняется из командной строки, то аргументы командной строки являются параметрами объекта ConnectionContext, в противоположном варианте значения берутся из других источников.
Для работы со встроенным объектом context указывается дополните-
льная синтаксическая конструкция SQLJ. В теле конструкции содержится определение соединения с базой данных. Обычно конструкция
используется для организации доступа к самой базе данных с использованием объектов класса ConnectionContext. Эти объекты содержат служебную информацию, такую, как тип драйвера, имя пользователя, пароль и т. д. Создание объекта ConectionContext является стандартной
операцией инициализации объекта, затем созданные СonnectionContext
объекты можно заставлять работать. В SQLJ-коде, ConnectionContext
объекты следуют непосредственно после #sql метки, в квадратных
скобках [ ] указывается имя созданного объекта контекста соединения,
желательно не путать данное определение с определением Java-массива.
import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
#sql public context MyContext;
Class. forName("COM.ibm.db2.jdbc.app.DB2Driver"). newInstance();
MyContext ctx1=DriverManager.getConnection ("jdbc:db2:sample");
MyContext ctx2=DriverManager.getConnection("jdbc:db2:sample",
"user", pwd");
...
#sql [ctx1] { SQL operation };
MyContext.setDefaultContext(ctx2);
...

Часть 5. Работа с базами данных при помощи Java 263
#sql { SQL operation };
#sql { SQL operation };
MyContext. getDefaultContext();
В 1-й строке создается объект MyContext, рожденный от встроенного
объекта context, в результате чего будет создан класс MyContext с несколькими конструкторами. Генерируемой код сделает более понятным
описанное ниже.
class MyContext extends sqlj.runtime.ConnectionContext{
public MyContext(String url, Properties info)throws SQLException;
public MyContext(String url)throws SQLException;
public MyContext(String url, String user, String password)
throws SQLException;
public MyContext(ConnectionContext conctx) throws SQLException;
public static MyContext getDefaultConnection();
public static void setDefaultConnection(MyContext myCtx);
public java.sql. DriverManager getConnection ();}
2-я и 3-я строки создают два разных объекта соединения с базой
данных для различных пользователей. У одного из подключаемых пользователей отсутствуют значения логина и пароля. Это указывает на использование значений данных атрибутов по умолчанию.
При создании нескольких разных соединений с базой данных воз-
можно использование единственного определенного объекта connectioncontext, который указывается в квадратных скобках после метки #sql —
строка 4, выполняющая для пользователя с отсутствующими параметрами соединения, присущие только этому пользователю SQL-операторы
после выполнения операций пользователя с параметрами, заданными по
умолчанию. Выполняются конкретные SQL-операторы (строки 6, 7) для
пользователя с именем «user» (строка 5). При помощи метода setDefaultConnection объекта MyContext в 8-й строке вызывается последний метод данного класса getDefaultConnection(), возвращающий значение текущего соединения. Перед использованием конструкторов класса необходимо указать драйвер базы данных непосредственно в коде, или через
файл свойств, или при трансляции указанием специальных аргументов
в командной строке. 1-ю строку обычно опускают и используют встроенный класс DefaultContext, без инициализации. Интерфейс ConnectionContext содержит ряд абстрактных методов, полезных при определении
соединения. Полезным здесь можно назвать метод освобождения ресурсов close(). Для получения информации о SQL-запросе используется метод getExecutionContext(). Получение информации о самом объекте ConnectionContext происходит при вызове метода getConnectionContext().
Каждый SQLJ-объект класса ConnectionContext включает конструктор,
использующий как аргумент объект класса Connection, описанный в JDBC.
Конструктор используется для создания объекта Connection. Доступ к объекту Connection можно получить, используя метод getConnection(). Другой
метод close() предназначен для освобождения системных ресурсов.
Константа CLOSE_CONNECTION указывается, если соединения с ба-
зой данных должны освободить ресурсы и соединение должно быть за-

264 Часть 5. Работа с базами данных при помощи Java
крыто. Другая константа, KEEP_CONNECTION, используется при обязательном сохранении соединения после выполнения части кода. Метод
isClosed() объекта connection context возвращает значение true, если вызывается любой вариант метода close().
37.1.1. Использование объекта Connection Context
Организация соединения с базой данных SQLJ-приложения может
осуществляться несколькими способами. Первый способ не использует
API JDBC для работы с базой.
1. Выполняется подпрограмма инициализации, описанная в создан-
ном классе #sql context ctx;
2. Вызывается конструктор класса Connection Context с параметрами
проинициализированными на первом этапе, имя драйвера, URL базы
данных и т. д.
ctx myconn=new ctx( jdbc:db2:SABPLEDB);
В другом способе доступ к базе данных осуществляется при помощи
объектов класса Connection, из пакета java.sql.Connection. SQLJ-приложение использует следующие шаги при встроенном объекте Connection
Context.
1. Выполняется подпрограмма инициализации, описанная в создан-
ном классе #sql context ctx;
2. Затем вызывается метод getConnection() класса java.sql.DriverMa-
nager с определенными параметрами, обеспечивающими доступ к базе:
Connection jdbccon= DriverManager. getConnection(jdbc:db2:SAMPLE);
3. Следующим шагом вызывается конструктор уже класса connection
context.
4. Наконец, вызывается конструктор для класса ctx, использующий
JDBC-объект connection как аргумент соединения:
ctx myconn=new ctx(jdbccon);
Каждый экземпляр Connection Contexts содержит объект класса
ExecutionContext, имеющий метод getExecutionContext(), используемый
для получения сведений о выполняемом операторе.
37.1.2. Модификация исполняемого SQL-оператора, используя объект
Execution Context
Для изменения SQL-запроса в стандарте SQLJ описан класс Executi-
onContext, имеющий методы по обработке SQL-запроса. Используя методы данного класса, приложение может получить сведения об исполняемом запросе, количестве строк выполненного запроса, о времени выполнения запроса и конструкции самого SQL-запроса.
ExecutionContext myExecCtx = new ExecutionContext();
#sql [myConnCtx, myExecCtx] { DELETE FROM emp WHERE sal > 3000};
#sql [myExecCtx] { DELETE FROM emp WHERE sal > 3000};

Часть 5. Работа с базами данных при помощи Java 265
Чаще всего работа ExecutionContext связана с работой потоков при вы-
полнении одного потока по обработке запроса, другой поток, пытающийся
обратиться к используемому оператору, блокируется. Нередко возникает
ситуация, когда вызов одной функции осуществляется посредством другой. И если оба ConnectionContext и ExecutionContext не определены, т. е.
используются значения, принятые по умолчанию, что является обычной
ситуацией, тогда SQLJ-операторы вызываемой функции используют
объекты Context, идентичные объектами другой функции, что может
привести к ошибке. После того как объект Execution Context привязали к
определенному SQL-оператору, можно выполнять методы объекта ExecutionContext. Например, используя метод getUpdateCount(), возвращающий количество обновленных строк данным SQL-оператором:
#sql TconnCtx, execCtxR {DELETE FROM EMP WHERE SALARY >
100000};
System.out.println(«Deleted» + execCtx. getUpdateCount() + «rows»);
Методы объекта ExecutionContext полезны, если необходимо полу-
чить исчерпывающую информацию о выполняемом запросе.
Метод getWarnings() — возвращает java.sql.SQLWarning объект, со-
держащий предупреждения выполнения SQL-операторов.
Метод getUpdateCount() — указывает количество строк, обработанных
последним SQL-оператором. Константа QUERY_COUNT возвращает количество обновленных строк. Другая константа, EXCEPTION_COUNT,
указывает на возникновение исключительной ситуации в момент исполнения последнего SQL-оператора.
Метод getNextResultSet() — указывает на следующую строчку сфор-
мированного ответа.
Метод getMaxFieldSize() — возвращает объект типа int, указываю-
щий максимальное число (в байтах), возвращаемое SQL-операторами
объекта. Значение по умолчанию ноль (0) говорит об отсутствии ограничений объема возвращаемого объекта.
Метод setMaxFieldSize() — устанавливает значение типа int, только
для колонок, содержащих объекты SQL типов BINARY, VARBINARY,
LONGVARBINARY, CHAR, VARCHAR или LONGVARCHAR.
Метод getMaxRows() — максимальное количество строк, обрабатыва-
емое итератором, как число типа int. Значение ноль (0) по умолчанию
указывает на отсутствие ограничений количества строк.
Метод setMaxRows() — указывает количество строк для модификации.
Метод getQueryTimeout() — определяет время в секундах как тип
int, по прохождении которого будет выполнен SQL-оператор, используемый в ExecutionContext. Значение по умолчанию ноль (0) снимает временной лимит.
Метод setQueryTimeout() — устанавливает значение выполнения
SQL-оператора как тип int.
Метод сancel() — прерывает поток существующего SQL-запроса.
В нижеуказанном примере показано использование некоторых мето-
дов класса ExecutionContext:
ExecutionContext execCtx=DefaultContext. getDefaultContext(). getExecuti
onContext();
-

266 Часть 5. Работа с базами данных при помощи Java
execCtx. setQueryTimeout(3);
#sql { DELETE FROM emp WHERE sal > 10000 };
System.out.println («removed» + execCtx. getUpdateCount() + «employees»);
В 1-й строке создается объект execCtx класса ExecutionContext, и
данному объекту присваивается значение выполняемого SQL-оператора. 2-й строка указывает промежуток времени, через который исполнится запрос, указанный в 3-й строке. Задержка по времени хороша
при многопотоковом запросе. И напоследок, 4-я строка выводит количество строк выполненного запроса.
Можно использовать в SQLJ-приложениях потоки, при этом может
возникнуть ситуация неправильной работы драйвера базы данных или
обращение к уже используемым данным.
Для предотвращения этой и подобных ситуаций лучше для объекта
ConnectionContext использовать различные объекты ExecutionContext.
Это позволит избежать ненужных тупиковых ситуаций, поскольку для
каждого объекта ConnectionContext будет создаваться свой поток. Если
поток выполнения SQL-оператора использует тот же ExecutionContext,
что и другой, в таком случае поток будет заблокирован, пока первый
поток не закончится. В результате достигается точность выполнения
SQL-операторов, а соответственно верность и ответов.
При использовании объекта класса ExecutionContext в SQLJ-прило-
жении делаются следующие шаги:
Создается объект ExecutionContext execCtx=new ExecutionContext();
Следом созданный объект execCtx будет ассоциирован с определен-
ным SQL-оператором для вызова объекта класса ExecutionContext. Имя
вызываемого объекта заключается в квадратные скобки, и затем будет
выполнен оператор. Можно ассоциировать различные объекты ExecutoionContext с определенным SQL-оператором
#sql execCtx {DELETE FROM EMP WHERE SALARY > 100};
При использовании в коде объектов двух классов ConnectionContext
и ExecutionContext объект класса ConnectionContext должен указываться первым.
Если в SQLJ-коде не указаны параметры доступа, то DB2 будет ис-
пользовать значение системы, на которой проинсталлирована база данных. Если необходимо выполнить SQL-оператор в другой базе данных,
необходимо создать объект ConnectionContext и передать ему параметры, необходимые для подключения к другой базе.
37.2. Iterator
SQLJ позволяет использовать повторитель (iterator). Итератор созда-
ется как Java-объект, т. е. имеет все атрибуты Java-классов, со своими
параметрами, указывающими типы объектов, а также, возможно, сами
объекты с которыми будет работать SQL-запрос. Итератор очень похож
на стандартный курсор в базе данных. Возвращаемое значение представляет набор строк из таблицы как Java-объектов. Курсор в базе данных используется для того, чтобы дать возможность пользователю про-

Часть 5. Работа с базами данных при помощи Java 267
сматривать результирующую таблицу строка за строкой. В отличие от
курсора, значения, полученные при помощи итератора, можно передать
методу для дальнейшей обработки. Существует два типа итераторов —
именованные и позиционные.
Именованный итератор указывает тип и имя колонки, а позицион-
ный итератор указывает только тип. Инициализация итератора всегда
должна быть раньше, чем его использование. Входящие переменные записываются в круглых скобках. Созданный объект класса Interator может также расширяться внешними интерфейсами при использовании
ключевого слова языка Java-implements. При этом обязательно следует
или импортировать пакет, содержащий данные интерфейсы, или же
указывать полное имя интерфейса — com.myPackage.myInterface. При
создании итератора необходимо соблюдать условие любви: сначала оговариваем, затем используем. Сначала объявляется итератор, затем создается объект созданного класса итератора.
#sql public iterator iterName (String name, date birthdate);
{iterName nameFromDB;
#sql nameFormDB {SELECT name, date FROM table};
String _s; Date _d;
while (nameFromDB.next())
{s=nameFromDB.name();
i=nameFormDB.birthdate();}}
В 1-й строке создается именованный итератор с двумя аргументами,
указывающими имя колонок, name и birthdate, и типы данных в этих
столбцах, String и date. Затем в строке 2 создается объект класса итератора iterName. В 3-й строке данному объекту присваивается значение
запроса из таблицы table. Используя метод netx(), строки 6 и 7, двум
переменным, 4, присваивается значение из таблицы.
37.2.1. Использование именованного итератора
Использование именованного итератора предназначено для выбора
определенных строк из результирующей таблицы. При создании именованного итератора, в SQL-запросе указываются имена колонок результирующей таблицы. Имена колонок, используемые в SQL-запросе и
имена колонок, используемые в именованном итераторе, должны быть
идентичны.
При определении именованного итератора в SQLJ-приложении в мо-
мент трансляции генерируется класс, содержащий специальный метод,
ассоциированный с каждой колонкой результирующего запроса. Метод
имеет эквивалентное имя колонки, с которой ассоциирован.
В момент выполнения SQL-запроса, указанного в именованном ите-
раторе, SQLJ-проверяет соответствие указанных имен метода и результирующей таблицы. Большой плюс именованного итератора состоит в
том, что последовательность указания колонок в итераторе может не
совпадать с последовательностью в результирующей таблице.
{#sql public iterator ByName(String LastName, Date HireDate);
ByName namiter;

268 Часть 5. Работа с базами данных при помощи Java
#sql nameiter={SELECT LASTNAME, HIREDATE FROM EMP};
String name;
Date hrdate;
while (namiter. next()) {
hrdate = namiter. HireDate();
name = namiter. LastName();
System.out.println(name + «was hired on» + hrdate); } }
В 1-й строке определяется именованный итератор с двумя перемен-
ными, имена которых являются именами колонок результирующей таблицы. Во 2-й строке создается объект класса итератор. На следующем
этапе созданному объекту присваивается значение SQL-запроса. Опять
же, имена колонок, из которых выбираются значения, такие же как и в
созданном итераторе. Java-переменные (поля), определенные в строках
4, 5, используются в дальнейшем для присвоения табличных значений.
9-я строка выводит результат на системную консоль.
Если оператор SELECT, используемый в именованном итераторе,
выбирает данные из таблицы, имена колонок не определены в Java, необходимо использовать псевдоним, зарезервированное слово AS. Такая
ситуация может возникнуть, например, при объединении таблиц.
Обычный SQL-оператор
SELECT «bad colname» FROM table,
но итератор не знает как работать с колонкой «bad colname», поэтому в
SQL запросе необходимо использовать псевдоним
SELECT «bad colname» AS GOODCOLNAME FROM GOODTABLE
Затем в приложении можно создавать итератор, с правильным име-
нем, будет использоваться псевдоним.
#sql public iterator ByName(String GoodColName);
ByName namiter;
#sql nameiter={SELECT «bad colname» AS GOODCOLNAME FROM
GOODTABLE};
Именованный повторитель работает только с именами колонок, по-
этому при объединенном запросе или при создании колонки, хранящей
данные вычислений из других колонок, необходимо использовать псевдонимы.
#sql emps = {SELECT maximum(comm, 0) AS maxComm FROM Emp};
В вышеуказанном SQL-операторе для этого используется ключевое
слово AS.
Классы, содержащие iterator c именованными значениями использу-
ют интерфейс NamedIterator, порожденный от ResultSetIterator, с одним методом next() который возвращает значение true, при условии существования следующей строки в результирующей таблице.

Часть 5. Работа с базами данных при помощи Java 269
37.2.2. Использование позиционного итератора
Другой тип итератора, описанный в спецификации, называется пози-
ционным итератором.
В отличие от именованного итератора, позиционный работает не с
именами колонок результирующей таблицы, а позициями колонок в
таблице. Позиции колонок перечисляются в определенном порядке слева направо. Тип объектов в колонке, указанной первым параметром
итератора, является первой колонкой с таким же типом в таблице. То
есть, если в таблице используется несколько колонок с различными типами данных, что вполне естественно, то первой колонке с определенным типом будет соответствовать переменная итератора того же типа.
Позиционный итератор используется для работы с SQL-оператором
FETCH.
#sql public iterator ByPos(String, Date);
Созданный позиционный итератор работает с колонками, содержа-
щими объекты типа string и Date.
Пример, рассмотренный выше с именованным итератором, с позици-
онным итератором выглядит несколько иначе.
{#sql public iterator ByPos(String, Date);
ByPos positer;
String name = null;
Date hrdate;
#sql positer = {SELECT LASTNAME, HIREDATE FROM EMP};
#sql { FETCH:positer INTO:name,:hrdate };
while (!positer. endFetch()):
{System.out.println(name + «was hired in»+hrdate);}
В первых строках создается позиционный итератор. Как можно заме-
тить, в нем отсутствуют имена колонок. До 5-й строки ничего важного не
происходит. Итератору присваивается значение результирующей таблицы, строка за номером 5. Дальше хуже. Используя SQL-оператор FETCH,
в программу передается следующее значение SQL-запроса, описанного в
6-й строке. Итератор, указанный в операторе FETCH, определяет, какая
строка результатов запроса должна быть считана. И ПОКА (while) данные в таблице существуют, о чем сообщает метод endFetch(), в 7-й строке, на экране можно увидеть имена всех, у кого дата.
Позиционный итератор очень полезен для получения данных одного
òèïà.
#sql iterator iterPos (String, double);
String u, v, w;
double x, y, z;
iterPos iter;
#sql iter={SELECT param1,aram2,param3 FROM table};
#sql {FETCH:iter INTO:u,:v,:w};
#sql {FETCH:iter INTO:x,:y,:z};

270 Часть 5. Работа с базами данных при помощи Java
Без учета уже известных строк 1—4 работа позиционного итератора
описана в коде, начинающемся с 5-й строки, в 6-й строке выбираются
все три значения типа String результирующей таблицы, а в 7-й все три
значения double той же таблицы. Используя один итератор с одним
SQL-оператором, можно очень гибко организовать работу с различными
запросами.
Каждый итератор использует системные ресурсы, используя метод
close(), можно добиться освобождения ресурсов без ожидания включения «сборщика мусора» Виртуальной Машиной Java(JVM).
Классы, содержащие inerator с позиционными значениями, реализу-
ют интерфейс PositionalIterator, расширенный от ResultSetIterator и добавочный методом endFetch(), прерывающим цикл FETCH... INTO.
37.2.3. Использование итератора для UPDATE и DELETE SQL-операторов
Использование итераторов возможно не только для получения дан-
ных из таблицы, но и для обновления и удаления данных.
SQL-операторы по изменению структуры таблицы — UPDATE и
DELETE — по-разному работают с таблицей. Использование данных
операторов в SQLJ-приложении несет свои ограничения. Для этого
обычно создается два различных SQLJ-файла. В первом файле создается Iterator, реализующий интерфейс sqlj.runtime.forUpdate:
#sql public iterator DelByName implements sqlj.runtime. ForUpdate(String
EmpNo);
Каждый создаваемый итератор должен иметь уровень доступа pub-
lic. Именно по этой причине необходимо создание двух различных классов. Созданным классом можно пользоваться для создания различных
объектов, основанных на обозначенном итераторе.
В нижеследующем примере в файле создается «удалитель», на осно-
ве вышеуказанного класса:
{ DelByName deliter;
String enum;
#sql deliter = { SELECT empno FROM emp WHERE workdept='D11'};
while (deliter. next()) {
enum = deliter. EmpNo();
#sql { DELETE WHERE CURRENT OF :deliter }; } }
1-я строка определяет класс deliter — своеобразный iterator. Создан-
ному объекту присваивается значение запроса. 3-я — проводится проверка на нахождение всех подобных объектов, и в 6-й, удаляются все
элементы с заданным значением. Весь фокус в том, что здесь отсутствует надобность вложенных запросов. «Делитеру» этот же самый итератор присваивает SQL-запрос, а затем SQL-оператор DELETE все
присвоенное удаляет.
Все классы, содержащие iterator, наследуют интерфейс ResultSetIte-
rator, в котором содержатся три метода: rowCount(), возвращающий количество строк, обработанных повторителем; метод close() — закрывает
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
