Содержание к диссертации
Введение
Глава I. О комбинаторных свойствах бесповторных формальных языков 18
1 Основные определения 18
2 Последовательность Аршона и ее структурные свойства . 23
3 Последовательность Аршона и порождение эндоморфизмом . 27
4 Экспонента избегаемости языка Аршона 30
5 Синтаксические конгруэнции бесповторных языков 33
Глава II. Об интеграции языка SQL с языками разработки кроссплатформенных систем 40
6 Обозначения и основные определения 40
7 Примеры типичного использования SQL и область применимо сти модели 41
8 SQL запросы и их абстрактное представление 44
9 Пакеты запросов и их изоморфизм 48
10 Менеджер запросов и наполнение пакетов 51
11 Технология SQLj 57
12 Интеграция с SQLj 65
Заключительные замечания 73
Литература 76
- Последовательность Аршона и ее структурные свойства
- Синтаксические конгруэнции бесповторных языков
- Примеры типичного использования SQL и область применимо сти модели
- Менеджер запросов и наполнение пакетов
Введение к работе
В данной работе рассматриваются вопросы, связанные с задачей поиска и обработки информации.
В общем виде эту задачу можно сформулировать следующим образом. Есть некоторый (обычно конечный) набор данных. Требуется выяснить, присутствует ли в этом наборе информация, удовлетворяющая определенным критериям. И если информация присутствует, извлечь ее в удобном для потребителя виде. Легко понять, что в абстрактном алгоритмическом смысле проблемы как таковой нет, поскольку определить за конечное время наличие некоторого объекта в конечном множестве безусловно можно. Но сложность в том, что нужно уметь решать задачу поиска и извлечения информации быстро, чтобы обеспечить адекватное для человека время реакции поисковой системы на введенный запрос.
Какими же способами можно решать эту задачу? По всей видимости, можно выделить два пути.
1. Можно пытаться оптимизировать сам алгоритм поиска, когда за счет
грамотного выбора структур данных, за счет использования паралле
лизма вычислительных систем уменьшается временная сложность ал
горитма.
К этому направлению можно отнести, например, хорошо известные автоматные алгоритмы поиска слов в тексте. Эти алгоритмы настолько важны, что фактически выделяются в отдельную область исследования. Так, алгоритмам сортировки и поиска посвящен отдельный том классического трехтомника Дональда Кнута "Искусство программирования" (см. [22]). В более современном учебнике по компьютерной математике "Алгоритмы: построение и анализ" (см. [23]) алгоритмы сортировки и поиска также составляют значительную часть материала.
Но построение эффективных алгоритмов невозможно без исследований на втором пути.
2. Можно пытаться анализировать критерии поиска и свойства множества
объектов, в котором поиск производится. И на основании этого анализа
из известных теоретических результатов сделать вывод о наличии или
отсутствии искомого объекта, сделать вывод о том, как провести поиск
более эффективно.
В рамках этого направления следует упомянуть, в частности, результаты теории формальных языков в области избегаемости. Так, например, по виду шаблона можно сразу же сказать, присутствует ли он в текстовой строке или нет.
В данной работе изложены результаты, имеющие непосредственное отношение к обоим названным направлениям.
Отметим, что формальные языки были упомянуты не случайно. Именно в терминах формальных языков удобно описывать задачу поиска. Поэтому изучение свойств формальных языков является важным для решения задачи поиска в целом.
Первая глава данной работы как раз посвящена изучению некоторых комбинаторных свойств формальных языков. В ней исследуются свойства бесповторных языков, устанавливаются взаимосвязи между комбинаторными свойствами и алгебраическими характеристиками объектов.
Вторая глава работы больше затрагивает алгоритмические вопросы. Но не с точки зрения оптимизации алгоритмов по скорости, а с точки зрения простоты реализации запросов на поиск и извлечение информации при помощи ставшего общепринятым языка запросов SQL. В этой главе приводится модель организации большой и функционально сложной програмы, позволяющая легко адаптировать ее под самые разнообразные системы хранения информации и дающая возможность повысить эффективность выполнения запросов SQL, используемых в программе.
Перейдем к более подробному изложению постановок задач и обзору полученных результатов.
Последовательность Аршона и ее структурные свойства
Слово Аш не содержит квадратов, то есть подслое вида хх для х Є +. Понятно, что тогда произвольное слово из LA также обладает этим свойством. Отметим ещё два свойства сверхслова Аш, связанные с повторяемостью его поде лов: Лемма 1.3 Каждое подслово сверхслова А ш повтпоряется в Аш счетпное число раз.
Доказательство. Действительно, пусть v Аш. Тогда г» w для подходящего3 к N0. И Wk получается из ui\ = 1 путём применения подстановки (1) к—1 раз. Но слово ui\ = 1 встречается в нечётных позициях Аш счётное число раз (тривиально проверяется с использованием лемм 1.1 и 1.2). Следовательно, и все "образы" этого слова должны встречаться в Аш счётное число раз. 23десь и далее пробелами выделено естественное разбиение слова на блоки 33десь и далее N = {1,2,..., п,...} — множество натуральных чисел; No = N U {0}; Z = {..., —2, —1,0,1,2,..., п,... } — множество целых чисел.
Слово, имеющее длину не большую 3fe, входит в Аш тогда и только тогда, когда оно входит в Wk+ь Доказательство. Пусть г; 3fe. Достаточно проверить, что если v Лш, то v Wk+ь- Легко понять, что при указанных условиях v и, где и получается из одной буквы применением к + 1 раз подстановки (1). Также непосредственно проверяется, что слово w содержит все буквы из как в четных, так и в нечетных позициях. Значит v и u k+i.
В этом параграфе мы попытаемся обозреть некоторые структурные свойства слова Аш и описать специальную конструкцию, позволяющую применять для изучения последовательности Аршона обычную индуктивную технику изучения сверхслов, определяемых D0L системами. Важность этой конструкции состоит вот в чем. Оказывается, несмотря на то, что в определении последовательности Аршона используется два эндоморфизма, несмотря на то, что последовательность Аршона невозможно породить при помощи одного эндоморфизма, тем не менее, удается показать, что на достаточно длинных словах преобразование, полученное из двух эндоморфизмов ведет себя как обычный эндоморфизм.
Начнем с обозрения важных для нас структурных свойств слова Аш. Для этого нам потребуется ряд новых понятий.
Пусть v и и — произвольные вхождения в Аш произвольных слов. Будем говорить, что v и и блочно синхронны слева (справа)у если вхождения v и и начинаются (кончаются) в блоках одной и той же чётности. Блочно синхронные слева и справа вхождения будем называть просто блочно синхронными. Будем говорить, что v и и синхронны слева (справа), если вхождения v и и начинаются (кончаются) в одной и той же позиции возможно различных блоков одной и той же чётности. Опять, вхождения синхронные и слева, и справа, будем называть просто синхронными.
Так, например, следующие вхождения слов и = 123 и v = 232 блочно синхронны слева: однако они не являются блочно синхронными справа. Обратим внимание, что понятия синхронизации относятся к конкретным вхождениям слов. Так, например, следующие вхождения слова v = 231 не являются блочно синхронными ни слева, ни справа:
Однако в некоторых случаях различные вхождения одного и того же слова могут оказаться синхронными:
Оказывается, синхронизация различных вхождений одного и того же слова в Aw непосредственно связана со структурой Аи. Лемма 1.5 Пусть v Є LA и \v\ 8. Тогда все вхождения v в Аы блочно синхронны.
Доказательство. Разберём возможные случаи. Пусть v\ и v2 — два различных вхождения слова v в Аш. Покажем, что V\ и v2 блочно синхронны слева. Действительно, поскольку два первых символа слова v однозначно определяют чётность блоков, Vi и г 2 не могут одновременно начинаться с первых двух букв блоков различной четности.
Пусть v\ начинается со второй, a t 2 — с третьей буквы блоков, то есть имеет место ситуация (здесь и далее пробелами показано разбиение на блоки): Vi = а,\а2 азД4а5 а6 . v2 = а\ а2аъ «5 6
Поскольку выделенные блоки содержат общее двухбуквенное подслово, они должны иметь одну и ту же чётность. Но, в Аш чётности соседних блоков всегда различны, следовательно вхождения V\ и v2 блочно синхронны.
Аналогично разбираются случаи, когда V\ начинается с третьего, a v2 — со второго или также с последнего символа блоков.
Осталось рассмотреть случай, когда v\ начинается с первой, a v2 — с последней буквы блоков (симметричный случай, когда v2 начинается с первой, a vi — с последней буквы блоков, рассматривается аналогично), и чётности этих блоков не совпадают. В этом случае имеет место следующая ситуация (без ограничения общности можем считать, что v = а\а2а ..., где аі «2,аз Є S): vt = a\a2az v2 = ax a2a3 ...
Заметим, что выделенные два символа а2аз однозначно определяют следующий символ в v (поскольку a\a2az образуют блок, а следовательно, а\ф а2ф Ф оз Ф сь\, и а2аз — начало блока). Значит, v = а\а2а$а,і..., и мы получаем следующую ситуацию: V\ = а\а2а$ 0\... v2 — а\ а2а а\... Но тогда, учитывая смену чётности у соседних блоков, выделенный символ аг однозначно определяет остаток блока. То есть v — аіа2а3аіава2 Продолжая рассуждать подобным образом и учитывая условие \v\ 8, получаем, что v с необходимостью имеет вид v — а\а2а3аіаза2аіа2 А для его вхождений v\ и v2 имеет место следующая ситуация:
Синтаксические конгруэнции бесповторных языков
В этом параграфе основным объектом исследования являются синтаксические и рисовские конгруэнции бесконечных факториальных бесповторных языков. Соответствующие понятия см. в 1.
В процессе изучения языка Аршона встал вопрос об устройстве его синтаксической конгруэнции. Оказалось, что ее можно точно описать в терминах рисовских конгруэнции.
Язык Аршона, как и другие языки подслов бесконечных сверхслов, конечно же является факториальным. Поэтому рассматривать рисовскую конгруэнцию по его дополнению в свободной полугруппе вполне корректно.
Оказалось (см. [37]), что синтаксическая конгруэнция языка Аршона совпадает с рисовской конгруэнцией по его дополнению до свободной полугруппы.
Аналогичный результат был ранее получен для бесповторного языка Туэ-Морса над двухбуквенным алфавитом (см. [28]).
Применяя технику, аналогичную использованной в названных работах, несложно показать совпадение синтаксической конгруэнции языка с рисовской по дополнению языка до свободной полугруппы для других бесповторных языков. В частности, это верно для языка Дежан, получающегося из сверхслова, описанного в [7], и также как и язык Аршона, обладающего минимальной возможной для трехбуквенного алфавита экспонентой избегаемо-сти.
В результате возникла гипотеза, что такое устройство синтаксической конгруэнции языка тесно связано со свойствами бесповторности и факто-риальности этого языка. И эта взаимосвязь действительно была найдена. Для формулировки соответствующей теоремы нам потребуется понятие равномерной рекуррентности.
Язык называется "равномерно рекуррентным, если для произвольного слова v Є L существует такое целое число nv, что v содержится в качестве подслова во всяком слове w Є L длины большей nv. Таким образом, если равномерно рекуррентный язык построен по некоторому сверхслову, то это означает, что для каждого подслова сверхслова последовательность расстояний между последовательными вхождениями подслова в сверхслово должна быть ограничена сверху.
Отметим для полноты картины, что часто определение равномерной рекуррентности формулируется следующим образом. Язык L называется равномерно рекуррентным, если для любого числа п можно указать число К(п) такое, что любое подслово w языка L длиной К{п) содержит все слова языка L длины не большей п, или, что то же самое, если истинен следующий предикат Vn Є N ЗК(п) Є N Vw Є L VvtL (\w\ = K{n) =ї v w) Легко понять, что оба данных определения эквивалентны. Теперь мы можем сформулировать, как выглядит найденная взаимосвязь между бесповторностью языка и устройстом синтаксической конгруэнции. Теорема 3
Синтаксическая конгруэнция бесконечного равномерно рекуррентного факториалъного языка ограниченной экспоненты совпадает с рисовской кон-грунцией по дополнению этого языка до свободной полугруппы.
Прежде чем перейти к доказательству, нам необходимо провести ряд промежуточных рассуждений. Пусть L — факториальный язык. Тогда pL С aL. Доказательство. Непосредственно из определения конгруэнции pL следует, что язык L является объединением ее классов. И поскольку синтаксическая конгруэнция языка является наибольшей конгруэнцией с таким свойством, то pL С OL.
Конгруэнции а и PL факториалъного языка L совпадают тогда и только тогда, когда все классы конгруэнции &L, составляющие язык L, одноэлементны.
Доказательство. В одну сторону утверждение доказывается тривиально, поскольку из равенства конгруэнции OL = PL И определения конгруэнции pi немедленно следует одноэлементность классов crjr,, составляющих L.
Обратно. Вложение рь Q &L следует из леммы 1.11. Покажем, что и вложение pL 2 &L Имеет МеСТО.
Пусть (и, v) Є 7. Если и 0 L, то из определения синтаксической конгруэнции v L. Но это значит, что и,ие4и (и,v) Є рь- Если же и Є L, то из предположения, что все классы конгруэнции, составляющие язык L, одноэлементны, следует, что и = v, и {и, v) = (и, и) Є pi. О
Пусть — язык, (и, v) 07,. Положим wn = pnuqn. Тогда для всех n Є No слова wn лежат в L. Более того, все они лежат в одном классе конгруэнции иi,. Доказательство. Заметим, что система слов {wk}keN0 удовлетворяет следующим соотношениям. Лемма 1.14 Если L — дмкториалъный язык, u,v Є L, и v, (u,v) Є rL, то язык L содержит все степени некоторого слова х Є L.
Примеры типичного использования SQL и область применимо сти модели
Легко понять, что эта простая по формулировке процедура является достаточно трудоемкой в реальности. Дело в том, что SQL-конструкции разбросаны по коду программы, они тесно интегрированы в основной язык разработки системы. Соответственно, слишком большой объем кода следует обработать только для того, чтобы найти нужные SQL-конструкции. Если бы удалось разорвать эту связь и собрать SQL-конструкции, скажем, в отдельный файл, задача стала бы существенно проще. Этим и вызвано требование Т2 в постановке задачи.
Приведенный пример также наглядно демонстрирует суть требования ТЗ. Действительно, ведь после исправления имени таблицы мы хотим лишь проверить корректность исправленных запросов. Но полномасштабное тестирование — единственный способ выполнить интегрированные в код программы запросы, единственный способ тестирования при подобной технологии. Поэтому в цикле разработки программного обеспечения следует оставлять достаточно большой запас времени на подобное полномасштабное тестирование. Так что требование ТЗ — это не что иное, как попытка снизить трудоемкость процесса разработки программного обеспечения.
Теперь рассмотрим другой пример. Он менее тривиален, но из него станет ясна суть требования ТІ.
Известно, (см. например, [15, 16]), что СУБД DB2 на разных платформах поддерживает разные диалекты SQL. Так, на платформах OS/390 и z/OS DB2 имеет специальный регистр, обратившись к которому, можно определить идентификатор (login) пользователя, от чьего имени в настоящий момент происходит работа с сервером. Отметим, что этот идентификатор может не совпадать с использованным при подключении к СУБД; его можно изменять динамически при помощи соответствующих команд SQL.
Пусть теперь программе требуется получить данный идентификатор. Для этого следует выполнить такой запрос SQL: VALUES (CURRENT SQLID) И тут же проявляются два нетривиальных момента, которые следует как-то обходить. 1. DB2 на платформах AS/400 и iSeries не поддерживает конструкцию VALUES (...). 2. DB2 UDB (платформы Linux и Win32) не поддерживает само понятие активного идентификатора пользователя. Вместо него есть понятие текущей схемы (CURRENT SCHEMA), но это не точный аналог.
То есть запрос, работающий на одной платформе, абсолютно непригоден для других из-за разницы в диалектах SQL. Хотя для бизнес-логики абсолютно неважно, каким способом получается активный идентификатор пользователя. Сама бизнес-логика вполне пригодна для всех названных платформ. Просто для DB2 на AS/400 и iSeries запрос должен быть, например, таким: SELECT CURRENT SOLID, COUNT( ) FROM SYSCAT.TABLES А для DB2 UDB в качестве активного идентификатора пользователя вполне годится идентификатор, который был указан пользователем при подключении к СУБД. Для его получения можно выполнить такой запрос: VALUES (CURRENT USER)
Получается, что запросы разные, а операция по сути одна: требуется вернуть строку с активным идентификатором пользователя. Именно эта операция требуется для бизнес-логики программы. И совершенно неважно, каким именно способом эта операция будет выполнена на конкретной СУБД. Так что если бы удалось отделить логику выбора используемого запроса от бизнес-логики программы, получился бы код, слабо зависящий от свойств конкретной СУБД. И так организованный код обладал бы очень полезным свойством расширяемости: если требуется расширить спектр используемых СУБД, следует лишь добавить специфичную для этой СУБД реализацию операций без изменения бизнес-логики. Причем описание операций дается на SQL. Следовательно, его может подготовить специалист по конкретной СУБД. И ему не нужно ничего знать про бизнес-логику и основной язык разработки системы. Выгода очевидна. Вот этим и мотивировано требование ТІ.
Осталось лишь сказать несколько слов про требование Т4. Но тут мотивация тривиальна: скорость выполнения программы является одной из важнейших ее характеристик. Так что любая технология, позволяющая повысить производительность, представляет ценность.
Прежде чем перейти к описанию идеи, лежащей в основе модели, требуется некоторым способом формализовать понятие SQL-запросов. Дать им формальное абстрактное описание, пригодное для реализации в рамках языка программирования и удобное для обсуждения модели.
Обратимся еще раз к примерам, приведенным в 7. Заметим, что при обсуждении примеров в том или ином виде прозвучали следующие фразы: - "следует отделить SQL-конструкции от кода программы" - "SQL-конструкции следует трактовать как операции, внешние для бизнес-логики продукта"
Из этого следует вполне закономерный вывод: нужно превратить каждый запрос на языке SQL в экземпляр некоторого класса, фиксирующего абстрактный вид понятия "запрос" , наделить этот класс необходимой функциональностью .
Назовем этот класс Query. И попытаемся выделить ту информацию, которая должна быть в него заложена. Заметим, что информацию требуется выбрать таким образом, чтобы она оказалась достаточной для работы со всеми основными типами запросов.
Безусловно, следующие элементы данных обязаны в том или ином виде присутствовать в структуре класса. 1. Текст SQL запроса с маркерами параметров (удобно хранить в виде строки). 2. Описание входных параметров (массив структур). Здесь каждому параметру уместно приписать следующие атрибуты: имя (позволит обращаться к параметру по имени); SQL тип параметра (позволит проверить совместимость типов данных); позиция в тексте SQL запроса (нужна для подстановки параметра); порядковый номер маркера параметра (для работы с параметром по имени). 3. Описание выходных полей набора данных (массив структур). Как правило, реально нужна лишь информация об SQL типах. Однако, есть смысл, как и в случае входных параметров, знать имя поля и его порядковый номер в наборе данных, чтобы предоставлять возможность доступа к полям выходного набора данных по имени, а не по их порядковому номеру. 4. Тип SQL запроса (Select, Update, Delete или Insert) Следует пояснить, что, например, в JDBC запросы на извлечение данных (Select запросы) и запросы на обновление (Insert, Update и Delete
Менеджер запросов и наполнение пакетов
Во-первых, поскольку статические запросы компилируются еще до запуска программы, они ощутимо быстрее выполняются в процессе ее работы, так как этапы синтаксического разбора и составления схемы выполнения запроса (компиляции) оказываются ненужными, они сделаны заранее. Во-вторых, более гибкая схема обеспечения безопасности данных, предоставляемая серверами DB2, доступна лишь при использовании статических запросов.
Тут следует дать более подробные пояснения. Обычно, чтобы иметь возможность выполнить запрос на извлечение данных с сервера баз данных, пользователь должен иметь права на доступ ко всем объектам, участвующим в запросе. Это означает, в частности, что если пользователю требуется информация из единственного поля большой таблицы, он должен иметь доступ ко всей таблице. Часто это оказывается нежелательно. И именно в подобной ситуации полезны статические запросы.
Дело в том, что для запуска статического запроса пользователю требуются лишь права на запуск этого запроса. Но при этом он может не иметь прав на объекты, участвующие в запросе. Тем самым, появляется возможность разрешить пользователю выполнять лишь определенный набор запросов на извлечение данных, не давая прав на все имеющиеся данные. Это повышает безопасность данных, хранящихся на сервере и упрощает администрирование системы.
Таким образом, применение технологии SQLj позволяет получить доступ к дополнительным возможностям СУБД, что безусловно является очень полезным качеством.
Однако у технологии SQLj есть и ряд недостатков.
1. Не все серверы баз данных поддерживают адаптацию профилей. Это значит, что хоть программа и сможет работать с таким сервером (за счет неадаптированной секции профиля), получить хоть какой-то выигрыш от технологии SQLj она не сможет. В то же время в ряде случаев функциональность, аналогичную функциональности статических запросов, можно получить другими методами. Например, на СУБД Microsoft SQL Server ее можно реализовать за счет использования хранимых процедур. Но в рамках SQLj это невозможно.
2. Адаптация профиля происходит автоматически, программист не имеет возможности влиять на этот процесс. Это значит, что при значительных расхождениях в диалектах SQL, технология SQLj не сможет выполнить подстройку инструкций SQL под конкретный сервер.
3. Инструкции SQLj являются инородными для языка Java, требуют особого расширения для файлов, требуют использования препроцессора, требуют дополнительных знаний в области технологии SQLj от программиста.
Видимо, именно из-за этих недостатков, даже имея ряд неоспоримых достоинств, технология SQLj не получила должной популярности среди разработчиков программного обеспечения.
Заметим, однако, что если бы удалось воспользоваться технологией SQLj так, чтобы обойти ее недостатки, в результате получился бы достаточно мощный инструмент. Именно по этому пути мы и пойдем. Для этого поступим следующим образом. Во-первых, мы сделаем так, чтобы программист не работал напрямую с SQLj, скроем этот уровень от программиста. Тогда недостатки технологии SQLj просто исчезнут, а достоинства останутся. Во-вторых, мы сделаем так, чтобы технология SQLj использовалась лишь тогда, когда это использование может принести реальную выгоду.
Вспомним, что рассматриваемая в этой главе модель подсистемы управления запросами изначально предполагает наличие полной информации о каждом SQL запросе, использующемся в программе. В частности, это означает, что необходимый SQLj код может быть построен автоматически. Формально это несложно. И в этом случае использование SQLj осталось бы прозрачным для программиста. Сложность в том, как правильно согласовать функциональность и программные интерфейсы SQLj со стандартными интерфейсами JDBC, которые являются более привычными для программистов. Таким образом, мы должны решить следующую задачу: по имеющимся описаниям SQL запросов требуется автоматически построить программный код SQLj, внешние интерфейсы которого были бы неотличимы от стандартных интерфейсов JDBC.
Отметим, что решение этой задачи изначально не может быть тривиальным. Дело в том, что мы формулируем и хотим решить задачу, отчасти противоречащую идеологии SQLj: технология предполагает статичность запросов, мы же хотим строить код SQLj динамически. Тем не менее, задача решается. И предлагаемый подход существенно расширяет сферу применимости технологии SQLj, поскольку позволяет использовать технологию SQLj для запуска в статическом режиме запросов, текст которых не известен на момент запуска программы. Подобное совмещение казалось бы несовместимых вещей является очень важным результатом этой главы.
Сначала опишем, чего хотим достичь. Мы хотим так строить SQLj код (и строить автоматически), чтобы в результате получить код, выполняющий следующие операции:
1. На входе есть соединение с сервером и имя запроса (напомним, что в рамках рассматриваемой модели каждый SQL запрос имеет свое имя, состоящее, в общем случае, из имени пакета и имени операции), на выходе хотим получить класс, позволяющий выполнить запрос с этим именем на данном соединении.
Поскольку SQLj код будет строиться автоматически, надо постараться сделать так, чтобы объем автоматически создаваемого кода был как можно меньшим. Для этого воспользуемся следующим полезным приемом: в автоматическом режиме будем создавать лишь существенную часть кода, и создавать ее будем в подклассах. Тем самым мы сможем описать основную функциональность нужных нам классов с использованием стандартных средств разработки. А в автоматическом режиме произойдет лишь наполнение этого скелета системы классов информацией о реальных запросах.
Нам потребуется два "скелетных" класса: класс для описания функциональности пакета запросов (назовем его SQLJPackage) и класс реализующий собственно установку параметров SQL запроса и его запуск (этот скелетный класс назовем SQLJStatement).
Класс SQLJPackage оказывается устроен достаточно просто: он лишь содержит таблицу сопоставления имен запросов именам подклассов класса SQLJStatement. Используя эту таблицу, класс SQLJPackage может по имени запроса создать экземпляр подходящего подкласса класса SQLJStatement для обслуживания запроса. Соответственно, в автоматически создаваемом коде нужно лишь построить подкласс класса SQLJPackage, и в этом подклассе переопределить метод заполнения таблицы соответствия.
Теперь обратимся к устройству класса SQLJStatement. Он оказывается устроен сложнее, чем класс для обслуживания пакета. Это связано с большим количестом реализуемых операций.
Начнем с того, что класс SQLJStatement должен хранить информацию о подключении к серверу, причем в удобном для использования с SQLj виде. Поэтому в классе SQLJStatement следует разместить ссылки на реализации интерфейсов Connection, ConnectionContext (создается по Connection) и ExecutionContext (создается по ConnectionContext). Все три класса необходимы для реализации нужной нам функциональности. Отметим один важный момент. SQLj декларирует лишь интерфейс ConnectionContext, но не его реализацию. Реализацию нужно создавать при помощи специальной инструкции SQLj в том же файле, в котором будет описываться подкласс класса SQLJStatement. Заранее создать реализацию этого интерфейса невозможно, поскольку она должна быть согласована с используемым транслятором SQLj. То есть соответствующая инструкция SQLj должна транслироваться одновременно с подклассом класса SQLJStatement.