Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:JAVA. Серверные приложения
.pdf
Часть 5. Работа с базами данных при помощи Java 271
iterator; а другой метод, isClosed(), проводит проверку закрытия iterator, возвращает значение true, если iterator закрыт. Все три метода вызывают SQLException исключение.
Совместное использование в итераторе SQL-операторов SELECT и
DELETE уменьшает количество кода, а значит, и увеличивает скорость
выполнения. Хорошо составленный SQL-запрос способен значительно
облегчить жизнь создателю SQLJ-приложений.
Спецификация SQLJ предоставляет возможность при помощи опера-
тора SET присваивать значения, передаваемые Java-переменным создаваемого приложения.
#sql { SET:x = foo1() + foo2() };
java.util. Date _d;
#sql {SET :_d=sysdate};
Типы переменных могут быть только OUT, затем с данными пере-
менными можно работать как с обычными полями. В вышеуказанном
примере переменной :x присваивается значение суммы функций, а переменной :_d присваивается значение системного времени базы данных.
При определении итератора или соединения их также можно иници-
ализировать одну или больше констант, включенных в определение
класса, расширять с помощью оператора with.
#sql public iterator MyIter with (var1=value1, ... , varN=valueN)
(type declarations);
Возможно использовать обе конструкции совместно, но конструкция
с implements должна определяться первой.
#sql public iterator MyIter implements mypackage.MyInterface
with (readable=true) (String empname, int empnum);
{ #sql iterator MyIter (String ename, int empno, float sal);
MyIter xy;
#sql xy = {SELECT name, number, salary FROM «Table»};
while (xy. next()) {System.out.println(xy)); } }
Важной частью является расположение SQLJ-деклараций итератора
и контекста соединения. Встроенные объекты можно разместить в любом месте программы, исключая расположение в теле ее методов.
По правилам Java один создаваемый класс должен иметь только
один класс с уровнем доступа public, поэтому при создании SQLJ-файлов с итератором и ConnectionContext необходимо создавать различные
файлы.
Если созданный файл определен с уровнем доступа public класс
(только один класс должен иметь уровень доступа public), то создаваемый SQLJ-файл должен именоваться по названию данного класса.
Так, если основной класс МуExample, то файл должен именоваться
как MyExample.sqlj, в противном случае название файла должно соответствовать правилам создания Java идентификаторов. Рекомендуется,
чтоб имя файла соответствовало названию основного класса. Для транс-

272 Часть 5. Работа с базами данных при помощи Java
ляции SQLJ-файла можно воспользоваться утилитой командной строкой SQLJ,
sqlj MyExample.sqlj — где MyExample.sqlj созданный файл,
так использовать и соответствующие инструменты, например, Stored
Procedure Builder DB2 или Visual Age for Java, поставляемые со встроенным транслятором.
import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
#sql iterator App_Cursor1 (String empno, String firstnme);
#sql iterator App_Cursor2 (String);
class App {static { try { Class.forName("COM.ibm.db2.jdbc.net.DB2Driver").newInstance(); }
catch (Exception e)
{ e.printStackTrace(); } }
public static void main(String argv[])
{ try {
App_Cursor1 cursor1; App_Cursor2 cursor2; String str1 = null; String str2 =
null; long count1; String url = "jdbc:db2:sample";
DefaultContext ctx = DefaultContext.getDefaultContext();
if (ctx == null)
{ try
{ Connection con = DriverManager.getConnection(url);
con. setAutoCommit(false);
ctx = new DefaultContext(con); }
catch (SQLException e) {System.out.println("Error:");
System.err.println(e); System.exit(1); }
DefaultContext.setDefaultContext(ctx); }
System.out.println("Successful connected.");
#sql cursor1 = {SELECT empno, firstnme FROM employee};
System.out.println("Received results:");
while (cursor1. next()) {
str1 = cursor1. empno(); str2 = cursor1.firstnme();
System.out.print ("empno= " + str1);
System.out.print ("firstname= " + str2);
System.out.print ("");}
cursor1. close();
#sql { SELECT count(*) into:count1 FROM employee };
if (1 == count1)
System.out.println ("There is 1 row in employee table");
else
System.out.println ("Into» + count1 + "rows in employee table");
System.out.println("Обновление.");
#sql { UPDATE employee SET firstnme = 'PAT' WHERE empno = '1' };
System.out.println("Обновление прошло успешно.");
str1 = "1";
#sql cursor2 = {SELECT firstnme FROM employee WHERE empno = :str1};

Часть 5. Работа с базами данных при помощи Java 273
System.out.println("результат:");
while (true)
{ #sql { FETCH:cursor2 INTO:str2 };
if (cursor2. endFetch()) break;
System.out.print ("empno= " + str1);
System.out.print ("firstname= " + str2);
System.out.print ("");}
cursor2. close();
System.out.println("Rollback the update.");
#sql { ROLLBACK work };
System.out.println("Rollback done."); }
catch( Exceptione){e.printStackTrace();} }}
Первые три строки указывают импортированные классы, благодаря
которым приложение может работать с SQLJ-конструкциями. В следующих двух строках, 4-й и 5-й определяются два итератора, один из которых именованный, другой позиционный. Блок из строк 6, 7, 8 определяет
драйвер базы данных, и, если драйвер отсутствует, возбуждается исключительная ситуация. 6-я строка предназначена для инициализации драйвера базы данных. В 7-й строке определяется драйвер доступа к базе
данных. В данном примере — это сетевой драйвер базы данных DB2. Если система определяет отсутствие такового драйвера (строка 8), то выводится значение стека, в котором описана причина и местоположение
ошибочной ситуации. 9-я строка описывает основной метод программы.
При обнаружении драйвера выполняется метод main(). Строка 11 описывает глобальные поля приложения. Два объекта, порожденных от итераторов, несколько строк и один URL, содержащий имя используемой базы
данных. Строка 12 описывает самый важный объект из всех вышеуказанных. В приложении используется объект ctx. Если созданный объект
ctx класса DefaultContext имеет пустое значение, т. е. параметры не заданы ни в командной строке транслятора, ни в других источниках — 14,
15, то создается объект con c указанным адресом (url) используемой базы
данных (jdbc:db2:sample). Строки с 20 по 21-ю обрабатывают ошибки, которые могут возникнуть в методе main(), и выводят сообщения о возникшей ошибочной ситуации. В 24-й строке создается запрос, чей результат
присваивается переменной cursor1 класса iterator, указанного в 4-й строке. Используя метод next(), глобальным полям присваиваются результаты запроса и выводятся на системную консоль 26, 27, 28, 29. В 30-й строке
подсчитывается количество строк в таблице. Полученное значение передается в программу при помощи переменой count1. В SQL-запросе выполняется функция count для поиска строк и количество строк присваивается переменной count1, строка 32. Последующие операторы проводят
проверку и выводят результат проверки. Для освобождения занятых ресурсов, используется метод close(), строка 47. В 36-й строке происходит
обновление таблицы при помощи SQL-оператора UPDATE. В 39 строке
инициализируется еще один объект итератора. По накатанному сценарию проходит обработка и этого SQL-запроса.

274 Часть 5. Работа с базами данных при помощи Java
37.3. Совместное использование встроенных объектов
Совместное использование iterator и объекта connection-context ис-
пользуется для получения в одном коде различных вариантов ответов.
Можно один запрос указать для разных таблиц или, наоборот, для одной таблицы использовать различные запросы различных пользователей.
#sql context MyContextNew;
...
MyContextNew ctx;
ctx=new MyContextNew( «Test» )
String name;
// Встроенная функция CREATE FUNCTION FIND_NAME (Name
STRING) RETURN STRING
#sql [ctx] name={VALUES FIND_NAME («user») };
...
#sql iterator myIter(String name);
myIter mi;
...
#sql [MyContextNew.getDefaultContext()] mi={SELECT name FROM
table WHERE name=’user’}
...
while( mi.next()){System.out.println(mi.name+«Name»);}
Создание объекта класса контекст, уже достаточно хорошо рассмот-
рено выше. Поэтому первые три (1, 2, 3) строки останутся без комментариев.
За счет использования встроенной функции FIND_NAME в базу дан-
ных передается переменная. Самое интересное начинается с 5-й строки,
где Java-переменной name присваивается значение встроенной функции
для пользователя указанного контекста. На следующем этапе создается
именованный итератор, строка 6. И, наконец, пользователю с именем
user предоставляется доступ к результатам запроса в 8-й строке.
Глава 38. Обработка SQL-ошибок и сообщений
При выполнении каждого SQL-оператора база данных присваивает
собственной переменной SQLCODE базы данных определенное значение, указывающие, как завершилось выполнение оператора.
Нулевое значение переменной SQLCODE указывает на успешное вы-
полнение SQL-запроса.
Отрицательное значение указывает но возникновение серьезной
проблемы при выполнении SQL оператора.
Положительное значение переменной SQLCODE говорит о ненорма-
льной ситуации, о которой должен быть извещен пользователь.
Для обработки ошибок в SQLJ-приложениях используются объекты
класса s java.sql.SQLException. SQLJ генерирует SQLException, когда

Часть 5. Работа с базами данных при помощи Java 275
SQL-оператор возвращает отрицательное или положительное значение
SQLCODE. Можно использовать метод getErrorCode()для получения
значения SQLCODE или метод getSQLState() для получения значения
SQLSTATE.
Для того чтобы SQLJ-приложение смогло обрабатывать ошибки, в
создаваемый код нужно импортировать класс java.sql.SQLException и
использовать Java-блок try/catch.
try {#sql {SELECT LASTNAME INTO:empname
FROM EMP WHERE EMPNO='133'}; }
catch(SQLException e) {
System.out.println("SQLCODE returned:" + e.getErrorCode());}
Так как выполняемый SQL-оператор потенциально может вызвать
ошибку, правильно написанная программа должна после каждого
SQL-оператора проверять значение переменной SQLCODE. Поскольку
для различных СУБД значения переменной SQLCODE различны то
лучше использовать другую переменную — SQLSTATE. Переменная
SQLSTATE состоит из двух частей двухсимвольного сообщения о возникшей ошибке и трехсимвольного элемента, указывающего конкретный тип ошибки.
Глава 39. Работа с базами данных
Работа любого e-commerсe приложения вплотную связана с базами
данных. СУБД является неотъемлемой частью корпоративных данных.
Созданию SQLJ-приложений способствуют две вещи: платформонезависимость Java и независимость SQL от используемой СУБД. Java
может создавать сетевые приложения, а SQL имеет встроенную поддержку архитектуры клиент\сервер. Любое e-commerсe приложение
делится на три логические части: пользовательский интерфейс, бизнесметоды, бизнес-данные. Если посмотреть на количественное соотношение пропорций уровней каждой логики, то наблюдается интересная
зависимость, наибольшее количество кода, около 70%, описывает пользовательский интерфейс, 20% — бизнес-логика и остальные 10% — данные. Картина меняется в противоположную сторону, если используется
хранение объектов пользовательского интерфейса в самой базе данных;
тут говорить о числах бесполезно. И все-таки употребление смеси SQL
и Java в одном приложении здорово сокращает код пользовательского
интерфейса, хранящегося в базе данных. Кроме стандартных операций
работы с базами данных, SQL, дополненный мощью Java, имеет прекрасные возможности по работе объектно-ориентированными возможностями, предоставляемыми современными базами данных. Совместное
использование SQLJ и UDF значительно упрощает создание Web-приложений, работающих с базами данных. А большое количество встроенных функций ускоряет разработку Web-приложений. Использование
концепции объектного ориентирования помогает созданию распределенных Web-приложений.

276 Часть 5. Работа с базами данных при помощи Java
Использование LOB дает возможность не ограничиваться стандарт-
ными представлениями вновь созданного типа, а использовать комплексную структуру.
Использование UDT позволяет разработчику создать новый тип дан-
ных. использованием UDF разработчик описывает поведение объекта
созданного нового типа.
39.1. LOB с использованием SQLJ
Каждый элемент базы данных хранится в колонке таблицы, а каж-
дая колонка определена типом данных, которые она содержит. В одной
колонке могут быть элементы только одного типа. Ограниченное количеств типов данных, с которыми работает база данных, не позволяет использовать неопределенные типы. DB2 содержит множество типов: буквенные строки, числовые данные, календарные и временные значения,
LOB, Nulls, графические строки, бинарные строки и связи.
В современных системах управления базами данных созданы новые
типы. Типы данных Large Objects (LOBs) предназначены для хранения
комплексных объектов, таких, как звук, динамические и статические
изображения, большие документы.
Большие объекты (LOB). Одной из самой важных характеристик бо-
льших объектов является их полный размер, именно полный размер, а
не ссылка на файловый объект, хранящаяся в базе данных. Это особенно касается мультимедийных приложений, статических изображений
видео- и аудио-данных. Одна минута полноэкранного видео занимает
около 30 Мбайт дискового пространства. Если учесть, что данный ролик
может находиться совершенно в другом месте, а значит, и работать с
ним, база данных будет по сети, жизнь системного администратора здорово усложнится, а клиент может очень долго ждать получения результата. Для того чтобы всем было хорошо, в базах данных и стали описываться большие объекты.
У каждой современной базы данных свои собственные типы объявле-
ния больших объектов, но смысл чаще всего один — хранение очень больших структурированных данных.
DB2 представляет три типа данных для хранения LOB-объектов как
строк длиной до двух гигабайт: single-byte Character Large Objects
(CLOBs) — для строк-символов и Double-Byte Character Large Objects
(DBCLOBs) — для строк, использующих двухбайтовую кодировку, такую же, как и все приложения Java, и, наконец, Binary Large Objects
(BLOBs) для двоичных объектов.
При размещении большого объекта в базе данных в каждый стол-
бец, содержащий LOB, помещаются дескриптор и описатель LOB, а
сами объекты хранятся вне базы данных. При такой политике обеспечивается сохранность физических данных в момент краха системы, с
другой стороны, ухудшается производительность системы при постоянном обращении к большим объектам. Представьте, сколько нужно
памяти, как физической, так и оперативной, для того чтобы 10 000
клиентов смогли посмотреть новейший популярнейший одноминутный

Часть 5. Работа с базами данных при помощи Java 277
ролик с участием автора книги. Почему 10 000? Это среднестатистическая цифра посещаемости среднего Web-сервера день. DB2 имеет прекрасную способность управлять размещением больших объектов на
внешних носителях. Более подробную информацию об этом удивительном свойстве можно почерпнуть из описания оператора CREATE
TABLE руководства разработчика СУБД DB2. Опять же, в DB2 есть
возможность восстановления больших объектов данных. Для каждого
из столбцов, содержащих LOB, в системном журнале создается регистрационная запись, гарантирующая точное восстановление объекта, в
случае необходимости данную возможность можно отключить в целях
экономии места в журнале. При отключении регистрации в контрольном журнале для столбца согласованность и последовательность транзакций для этого столбца тем не менее гарантируется, но тогда восстановление данных этого столбца не смогут быть обновлены корректно
базой данных, а будут возвращены данные последней успешной транзакции.
В связи с большими размерами LOB очень желательно, чтобы коли-
чество операций перемещения и копирования с этими данными было
минимально, чем обычно пренебрегают системные архитекторы, сохраняя клиентскую сессию в едином BLOB стандартного размера. И не
важно, сколько шла сессия, десять секунд регистрации или же выбора
продуктов, размер BLOB всегда задается в момент создания таблицы.
И после всего этого пишется очень большое количество ненужных хранимых процедур, выделяющих данные из созданного BLOB. В большинстве случаев именно незнание архитектором всех возможностей используемого инструментария порождает массу дополнительных решений
повышения производительности системы в целом. Еще одной ошибкой
системных архитекторов можно считать неправильный выбор между
хранимой процедурой и внешним приложением для обработки больших
объектов.
В DB2 существует специальный объект, называемый LOCATOR-ука-
зателем, содержащий значение большого объекта, но не содержимое самого объекта. Указатель содержит описание способа, с помощью которого при необходимости может быть получено значение LOB. Указатель
можно использовать в любом SQL-операторе, эффективность оператора
основывается на том, что указатель работает со значением BLOB, а не с
его содержимым, чем опять же пренебрегают системные архитекторы,
предпочитая работать с полным объектом, при этом используя встроенные запросы. С использованием указателей прикладная программа может выполнить манипуляцию над значением большого объекта, отложив материализацию результата в виде большого объекта до конечного
этапа.
Одной из самых распространенных операций по работе с LOB явля-
ется помещение большого объекта во внешний источник, файл и экстракция из внешнего файла и помещение большого объекта в базу данных. Фотография помещающаяся на динамической Web-странице при
выборе определенного условия. В обычных приложениях ссылка на
файл, содержащий большой объект, оформляется в виде строкового
значения, содержащего путь к данном файлу. Затем файловая ссылка

278 Часть 5. Работа с базами данных при помощи Java
будет использоваться в SQL-запросе как в качестве входной, так и в
качестве выходной переменной. Использование же хранимой процедуры в одном контексте даст преимущества без обращения непосредственно к внешнему файлу. С использованием указателей и файловых
ссылок приложения очень часто могут обрабатывать большие объекты,
даже, собственно, не выбирая сами объекты из внешних носителей. Ситуация меняется на противоположную, когда клиенту действительно
необходимо работать с большим объектом, а не со значением этого объекта. Чаще всего такая ситуация возникает при использовании ГИС
систем, например, для отображения участка поверхности и дальнейшего моделирования ландшафтного изменения или, например, проектировочных работ связанных с обработкой большого количества графических изображений.
Именно для решения работы непосредственно с большими объектами
компания IBM расширила свою СУБД различными типами реляционных расширений, предназначенных для обработки LOB. На настоящий
момент IBM поставляет шесть различных расширений:
Text Extender — поддерживает очень быстрое извлечение больших
текстовых данных на основе их содержания. Документы могут быть
любого формата — Microsoft Word, WordPerfect и AMI PRO. Необходимо еще раз отметить: при использовании расширения большие объекты
хранятся в самой базе данных. Это значит, что SQL-запрос может содержать в себе условия, касающиеся содержимого документа, с условиями, связанными с другими столбцами таблицы. Обычный SQL-запрос.
Image Extender — предназначено для сохранения и извлечения раз-
личных изображений в нескольких популярных форматах GIF, BMP и
JPEG, а также преобразования изображения из одного формата в другой. Так же как Text Extender, Image Extender хранит объекты в базе
данных только в миниатюрном варианте полного изображения для просмотра.
Video Extender работает, как и два вышеперечисленных, т. е. поме-
щает данные непосредственно в базу данных, но используются только с
видео форматами данных MPEG, AVI, и QUICKTime. Video Extender
позволяет автоматически разбить запись на отрезки на основе обнаружения изменения фона, а также извлечь репрезентативный кадр для
каждого отрывка.
Audio Extender использует аудио форматы AIFF, MIDI и WAVE, и
работает на основе аудиоданных.
Fingerprint extender — позволяет сохранять и извлекать отпечатки
пальцев в специальном формате, а также осуществлять поиск отпечатков пальцев, схожих с представленным образцом.
XML Extender — позволяет хранить XML-документы в столбцах
нового специального типа данных. Предоставляет возможность разложить XML-документ на компоненты и хранить их в отдельных столбцах нескольких таблиц. В обоих случаях для осуществления быстрого
поиска можно определить индексы для элементов а атрибутов XMLдокумента.

Часть 5. Работа с базами данных при помощи Java 279
Каждая таблица DB2 может иметь большое количество LOB-данных.
И любые из этих данных могут быть максимального размера — 2 Гбайт.
При этом простая строка может содержать 24 гигабайта LOB-данных, а
емкость самой таблицы DB2 составляет 4 терабайта.
При все этой монстрообразности данные объекты обрабатываются
òàê æå, êàê стандартные примитивы è обычными операторами
(SELECT, INSERT, DELETE, UPDATE), и встроенными функциями.
Проблемы начинаются при использовании больших объемов LOB-данных с маленьким объемом физической памяти сервера. Для устранения
больших временных задержек приходится очень активно манипулировать LOB-данными. Одним из решений данной проблемы является сохранение LOB-данных в файлах или же просто наращивание физической памяти до нужного размера.
Character Large Objects (CLOBs) — буквенная строка, состоящая
только из текстовых, однобайтных символов, т. е. не UNICODE. Этот тип
лучше всего подходит для хранения текста с типом данных VARCHAR.
Данные могут быть в любой кодировке, требующей для представления
один байт (ASCII).
Double-Byte Character Large Objects (DBCLOBs) — текстовая строка
данных, содержащая двухбайтное представление символов. Все Javaобъекты, символы, строки и идентификаторы состоят из 16-разрядных
кодов, т. е. двух байт.
Binary Large Objects (BLOBs) — бинарная строка битов, очень по-
лезна для сохранения бинарных данных (человек их обычно не может
просто прочесть), таких, как звук, изображение, исполняемый модуль
ит.д.
Основная часть приложений в последнее время работает с большими
объектами — Large Objects (LOB), такими, как бинарные объекты
(BLOB), буквенные (CLOB), двухбайтные буквенные (DBCLOBs). Для
обработки больших объектов данных в SQLJ предусмотрены дополнительные классы. Следует отметить различия, существующие на настоящий момент у различных производителей баз данных в характеристике
больших объектов, что никак ни отразилось на принятии стандарта
SQLJ. Принцип работы с LOB практически везде сохраняется. Не существует добавочных операторов по работе с большими объектами, и нет
никаких ограничений, связанных с ними.
Пакет sqlj.runtime содержит три класса по работе с потоками. Пото-
ки могут использоваться для работы с большими объектами. Все три
класса наследуются от стандартного класса java.io.InputStream.
•
BinaryStream(InputSteram in, int length)
•
AsciiStream(InputSteram in, int length)
•
UnicodeStream(InputStream in, int length)
Эти классы можно использовать для присваивания переменным зна-
чений, сохранении этих значений в базе данных или просто для обработки на получение какого-либо результата. Для получения данных из
бинарного файла лучше всего воспользоваться потоком BinarySteram.
Два других потока предназначены для работы с текстовыми данными.
Один из них, AsciiStream — для текста, представленного в ASCII кодировке, другой, UnicodeStream, — для UNICODE. Все три класса имеют

280 Часть 5. Работа с базами данных при помощи Java
похожие конструкторы с двумя параметрами, объектом класса InputStream и указателя длины объекта. Указатель является положительным
числом примитивного типа int. Вместо InputStream можно использовать
любой класс, порожденный от java.io.InputStream.
java.io.File myFile=new java.io.File("/image/image/profile.gif");
int length=(int) myFile. length();
Java.io.FileInputStream fis=new java.io.FileInputStream(myFile);
Sqlj.runtime. BinaryStream bis=new sqlj.runtime. BinaryStream( fis, length);
#sql {INSET INTO table (colname) VALUES (:bis) };
bis. close();
Для краткости здесь опуoщены операторы импортирования. Первые
четыре строки — стандартные инициализационные действия Java. 5-я
строка — SQLJ-оператор, исполняющий добавление в таблицу файла
profile.gif. Далее, строка 6 — поток, и происходит закрытие потока, в
котором находится изображение.
Прочитать данные из большого объекта тоже не представляется
сложным:
#sql iterator myIterator (Sting name, sqlj.runtime.BinaryStream image);
myIterator iter; String nname; Sqlj.runtime.BinaryStream bis;
#sql iter
SELECT name, image FROM table};
while (iter.next())
{nname=iter.name;
bis=iter.image
bis.close();}
iter.close();
Вначале создается именованный повторитель с двумя аргументами.
Во 2-й строке инициализируются поля. 3-я строка является SQL-оператором, выполняющим запрос из таблицы изображения и его имени.
И, наконец, созданным полям присваиваются значения из таблицы. 7я и 8-я строки освобождают ресурсы, занятые потоком и повторителем.
Немаловажной деталью является работа с большими объектами
при использовании различных версий JDBC-драйверов доступа. Только в новой версии JDBC 2.0 встроена поддержка работы с большими
объектами и графическими типами данных. Например, СУБД DB2 работает с большими объектами, используя драйвер JDBC 1.2. При этом
необходимо учитывать лимит возвращаемых значений больших объектов.
39.2. SQLJ и пользовательские типы данных UDT
При использовании только встроенных базовых типов данных разра-
ботчик ограничен количеством этих типов. Для DB2 их порядком 16,
представляющих самые распространенные типы, используемые в программировании. Но в каждой компании существуют свои специальные
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
