Рассмотрим теперь каждый шаг подробно. ПОДГОТОВКА ДОСТУПА К ДАННЫМ Все основные настройки IDAPI хранятся в специальном конфигурационном файле. Такой способ обеспечивает дополнительную мобильность приложений. При переносе в другую среду не требуется менять само приложение достаточно настроить конфигурационный файл. Конфигурационный файл (обычно IDAPI.CFG) содержит описание и настройки всех доступных драйверов, настройки IDAPI и описание определяемых пользователем алиасов. Алиас - это ссылка на конкретную БД по короткому имени. Использование алисов позволяет легко менять местоположение и тип рабочей БД. Помимо местоположения БД алиас содержит тип и настройки драйвера, который используется для доступа к данным. ИНИЦИАЛИЗАЦИЯ IDAPI На первом шаге приложение всегда вызывает функцию DbiInit, которая инициализирует IDAPI. Приложение может передать функции DbiInit указатель на структуру DBIEnv, которая содержит начальные установки: рабочую директорию, язык на котором будут выдаваться сообщения об ошибках, имя пользователя, полное имя конфигурационного файла. Но если этот указатель равен NULL, то IDAPI будет использовать настройки по умолчанию, а конфигурационный файл (IDAPI.CFG) будет искаться в следующей последовательности: Преверяется константа CONFIGFILE01 в секции [IDAPI] файла WIN.INI: Проверяется стартовая директория; Если конфигурационный файл не найден, то IDAPI использует настройки по-умолчанию, при этом доступ к SQL серверам не возможен. ПРИМЕР // инициализация IDAPI rslt=DbiInit(NULL); Кроме инициализации вызов DbiInit открывает новую сессию. Сессию позволяет изолировать операции доступа к БД без необходимости запускать новую копию приложения. Все дальнейшие действия приложения будут происходит внутри контекста сессии. С точки зрения базы данных, каждая сессия выглядит для нее как отдельный пользователь. К примеру, запросы на блокировку из одной сессии конфликтуют с блокировкой, установленной другой сессией даже в том случае, когда обе сессии принадлежат одному и тому же клиенту. Сессии - это способ полностью изолировать одно действие с базой данных от другого. Перед тем, как действие с БД будет выполнено в рамках сессии, должна быть открыта одна или более БД. Сессия может одновременно иметь любое количество открытых БД. Каждая сессия может быть настроена независимо в отношении таких вещей, как Call-back-функции, языки для сообщения об ошибках, формат данных и т.д. ОТКРЫТИЕ БАЗЫ ДАННЫХ После успешной инициализации IDAPI, приложение открывает базу данных (БД) вызовом функции DbiOpenDatabase. Успешный вызов этой функции возвращает идентификатор БД, который затем используется в большинстве других функций IDAPI. Для подключения к SQL БД вызов функции DbiOpenDatabase должен содержать имя пользователя и пароль. Следующий пример демонстрирует открытие стандартной БД (Paradox, dBASE, ASCII), помещая нулевой указатель вместо имени БД и ее типа. rslt=DbiOpenDatabase(NULL, NULL, dbiREADWRITE, dbbiOPENSHARED, NULL, 0, NULL, NULL, &hDb); Для того чтобы изменить текущую директорию для стандартной БД используется функция DbiSetDirectory rslt=DbiSetDirectory(hDb, "C:\DATA"); При открытии SQL БД удобно задавать имя БД, ее местоположение и и настройки IDAPI в алиасе. Следующий пример открывает именованную БД на SQL сервере. rslt=DbiOpenDatabase("myalias", NULL, dbiREADWRITE, dbiOPENSHARED, "mypassword", 0, NULL, NULL. &hDb); ОТКРЫТИЕ ТАБЛИЦЫ ДАННЫХ После открытия БД приложение открывает таблицу вызовом DbiOpenTable . Входными параметрами для этого вызова являются: имя таблицы, тип драйвера, индекс (по которому будут отсортированы записи), права доступа, способ преобразования данных. В случае успешного выполнения DbiOpenTablе возвращает идентификатор курсора открытой таблицы. В IDAPI курсор - это основное средство доступа к данным. Получить идентификатор курсора означает получить доступ к данным. Следующий пример открывает Paradox таблицу "MyTable": DbiOpenTable(hDb, "MyTable", szPARADOX, NULL, NULL, 0, dbiREADWRITE, dbiOPENSHARED, xltFIELD, 0, NULL, &hCursor); Посмотрим повнимательнее на входные параметры вызова DbiOpenTable.
Под правами доступа подразумевается право на запись (параметр eOpenMode) и право владения данными (параметр eShareMode): общее или исключительное. Права доступа к данным задаются в трех ключевых местах (перечислены в порядке убывания приоритета): Опция OPEN MODE в файле конфигурации IDAPI.CFG Входные параметры eOpenMode и eShareMode в функции открытия БД DbiOpenDatabase. Входные параметры eOpenMode и eShareMode в функции открытия таблицы DbiOpenTable. Как видно, права доступа, указанные в вызове DbiOpenDatabase, имеют больший приоритет, чем при вызове DbiOpenTable. Например, если БД была открыта только для чтения, то никакая таблица в этой БД не может быть открыта для записи. Для того, чтобы таблица могла быть открыта для записи, необходимо чтобы соответствующая БД была открыта для записи. Аналогичное правило действует и для параметра eShareMode, определяющего будет ли доступ к данным разделяемым или исключительным. ПОЛУЧЕНИЕ ИНФОРМАЦИИ О СВОЙСТВАХ КУРСОРА. Открыв таблицу и получив идентификатор курсора, приложение может получить полную информацию об открытой таблице. Для этого приложение вызывает функцию DbiGetCursorProps c идентификатором курсора в качестве входного параметра. Вызов DbiGetCursorProps обычно следует сразу за вызовом DbiOpenTable. Вызов функции DbiGetCursorProps заполняет структуру CURProps, которая содержит следующую информацию о курсоре (таблице): структура таблицы (имя, количество полей, количество проверок правильности значений, количество проверок целостности, и др.); особенности работы IDAPI с данной таблицей (логическая длина записи, физическая длина записи, размер буфера для ключа, способ трансляции данных, имя языкового драйвера, количество используемых фильтров, тип курсора, и др.) ПОДГОТОВКА БУФЕРА ЗАПИСИ Операции обмена информацией происходят в IDAPI через буфер записи, поэтому перед тем, как использовать курсор для доступа к данным в таблице, необходимо подготовить этот буфер. Подготовка буфера записи включает в себя резервирование памяти и в некоторых случаях ее инициализацию. Инициализацию буфера записи необходимо выполнить перед тем, как вставить в таблицу новую запись. Инициализацию буфера осуществляет функция DbiInitRecord. Эта функция корректно инициализирует запись пустыми полями в соответствии с их типом. Для таблиц в формате Paradox функция DbiInitRecord использует при инициализации значения по умолчанию (если последние указаны в таблице). ПРИМЕР Следующий пример демонстрирует то, как получить информацию о свойствах курсора, зарезервировать память для буфера записи и массива дескрипторов полей и получить сами дескрипторы. DBIResult rslt; pCHAR pRecBuf; CURProps curProps; pFLDDesc pFldArray; . . . //Получаем информацию о свойствах курсора rslt=DbiGetCursorProps(hCursor, &curProps); if (rslt==DBIERR_NONE) { //Резервируем память для буфера записи pRecBuf = malloc(curProps.iRecBufSize); // Проверяем результат функции malloc . . . //Получаем массив дескрипторов полей pFldArray = (pFLDDesc) malloc(sizeof(FLDDesc) * curProps.iFields); // Проверяем результат функции malloc rslt = DbiGetFieldDescs(hCursor, pFldArray); . . . } ПОЗИЦИОНИРОВАНИЕ КУРСОРА И выборка ЗАПИСей Одна из сильных сторон технологии IDAPI - это реализация навигационных
возможностей. Для этой цели предусмотрены как функции, выполняющие
только позиционирование, так и функции, осуществляющие одновременно
и позиционирование и выборку записи в буфер. ПОЗИЦИОНИРОВАНИЕ КУРСОРА В ПРОМЕЖУТОК МЕЖДУ ЗАПИСЯМИ. В IDAPI предусмотрены функции, которые позиционируют курсор в промежуток между записями: перед первой записью, в начале файла (DbiSetToBegin); в конце файла (DbiSetToEnd); перед первой записью с заданным значением ключа (DbiSetToKey). Позиционирование курсора в промежуток между записями может значительно упростить программирование. Например, вызовом функции DbiSetToBegin Вы позиционируете курсор перед первой записью в таблице. Затем Вы можете организовать цикл обработки всех записей в таблице с помощью функции DbiGetNextRecord. (А если бы курсор был позиционирован на первую запись, то циклический вызов функции DbiGetNextRecord пропустил первую запись.) ПОЗИЦИОНИРОВАНИЕ КУРСОРА НА ЗАПИСЬ И ВЫБОРКА ЗАПИСИ. Кроме функций, позиционирующих курсор в промежуток между записями, в IDAPI содержатся функции, которые указывают непосредственно на запись. Если приложение подготовило буфер записи, то эти функции могут также осуществлять выборку данных. Большинство таких функций позволяют заблокировать запись от доступа другими приложениями. Запись будет оставаться заблокированной до тех пор пока приложение явно не позаботиться об этом или до конца сессии. Ниже приведено краткое описание этих функций ПРИМЕР В следующий примере курсор устанавливается на начало файла, и затем осуществляется просмотр всей таблицы: //Устанавливаем курсор на начало файла DbiSetToBegin(hCursor); //Просматриваем всю таблицу while(DbiGetNextRecord(hCursor, dbiNOLOCK, pRecBuf, NULL) == DBIERR_NONE) { . . . }
ПОЗИЦИОНИРОВАНИЕ КУРСОРА С ПОМОЩЬЮ ЗАКЛАДОК При перемещении по таблицам удобно пометить некоторое положение курсора, с тем чтобы в дальнейшем к нему вернуться. Реализацию такой возможности предоставляют закладки. Закладка это некоторое положение курсора, которое хранится в буфере закладки во внутреннем представлении. Буфер закладки всегда резервируется самим приложением, поэтому количество закладок в принципе не ограничено. IDAPI позволяет сравнить одну закладку с другой и выяснить их относительное положение. Не для всех типов данных IDAPI гарантирует точный возврат к закладке. Правильный возврат курсора гарантируется только в том случае, если закладка является стабильной. Если закладка не стабильная, то другое приложение, модифицируя таблицу, может изменить настройку закладки. Необходимую информацию для организации закладки (стабильность закладки, размер буфера, необходимого для хранения закладки) содержит структура CURProps, которую заполняет вызов DbiGetCursorProps (см. ПОЛУЧЕНИЕ ИНФОРМАЦИИ О СВОЙСТВАХ КУРСОРА). Структура CURProps содержит следующие поля, описывающие свойства закладок:
Стабильность закладок зависит от типа драйвера следующим образом:
ДОБАВЛЕНИЕ, ОБНОВЛЕНИЕ И УДАЛЕНИЕ ЗАПИСЕЙ Прежде всего, для того чтобы добавить, модифицировать или удалить запись, надо быть уверенным в том, что: курсор, связанный с таблицей, имеет права записи; запись не заблокирована другим пользователем. ДОБАВЛЕНИЕ ЗАПИСИ Для того, чтобы добавить новую запись в таблицу, приложение должно выполнить следующие шаги: Создать пустую запись в буфере записи вызовом функции DbiInitRecord. Проинициализировать поля создаваемой записи в буфере записи (еще не в таблице) с помощью функции DbiPutField. Записать содержимое буфера записи в таблицу, используя функции DbiAppendRecord (добавление новой записи в конец таблицы) или DbiInsertRecord (вставка новой записи в таблицу). При использовании функции DbiInsertRecord приложение может заблокировать вставляемую запись. ОБНОВЛЕНИЕ ЗАПИСИ Модификация существующей записи осуществляется следующим образом: Прочитать требуемую запись в буфер записи (установив блокировку, если требуется). Изменить содержимое обновлямых полей в буфере записи с помощью функции DbiPutField. Записать содержимое буфера записи в таблицу, используя функцию DbiModifyRecord. Приложение должно указать будет ли снята блокировка с записи после завершения DbiModifyRecord или нет. УДАЛЕНИЕ ЗАПИСИ Удаление существующей записи происходит следующим образом: Установить курсор на удаляемую запись. Удалить запись с помощью функции DbiDeleteRecord. Если приложение имеет буфер записи, то удаленная запись будет скопирована в буфер. После удаления записи курсор указывает на промежуток между записями, которые находились вокруг удаленной. Удаление записей имеет некоторые особенности для разных форматов данных. Для таблиц в формате dBASE удаленная запись не уничтожается в таблице до тех пор, пока приложение не вызовет функцию DbiPackTable. Из таблицы в формате Paradox нельзя удалить запись, если это может привести к нарушению целостности межтабличных связей. Например, если курсор в главной таблице указывает на запись, которая содержит значения, связанные с подчиненной таблицей, то вызов DbiDeleteRecord закончится неудачей и положение курсора не изменится. ОПЕРАЦИИ С НЕСКОЛЬКИМИ ЗАПИСЯМИ IDAPI предоставляет две функции для добавления, обновления и удаления сразу нескольких записей: DbiBatchMove и DbiWriteBlock. Функция DbiBatchMove может использоваться для добавления, обновления и выделения записей из одной таблицы в другую. Причем исходная таблица и таблица получатель могут быть разных типов. Имеется возможность скопировать таблицу одного типа в новую таблицу другого типа. Функция DbiBatchMove также может использоваться с текстовым драйвером для импорта и экспорта данных из/в любой другой поддерживаемый формат. Под термином BLOB поле (большой двоичный обьект) обычно понимают большой массив информации (например изображение, звук ,...), находящийся в некотором поле записи. IDAPI предоставляет специальные средства для работы с BLOB полями. Для выборки записей, содержащих BLOB поля, разработчик должен использовать те же вызовы, что и для обычных записей. А для обработки самих BLOB'ов IDAPI предоставляет ряд специальных функций ( DbiOpenBlob, DbiGetBlob, и др.). Рассмотрим основные моменты работы с BLOB полями. Перед любым действием с BLOB полем приложение должно открыть BLOB поле. Для этого приложение вызывает функцию DbiOpenBlob и передает в качестве входных параметров идентификатор курсора, указатель на буфер записи, порядковый номер BLOB поля в записи и права доступа. Функция DbiOpenBlob поместит идентификатор открытого BLOB поля в буфер записи. Только после успешного вызова функции DbiOpenBlob приложение может использовать другие функции, работающие с BLOB поле. Перед открытием BLOB поля для чтения/записи рекомендуется блокировать запись. Это гарантирует отсутствие проблем в том случае, если другое приложение попытается изменить ту же самую запись. Открыв BLOB поле и получив его идентификатор, приложение может определить размер BLOB поля с помощью вызова DbiGetBlobSize и затем прочитать данные (DbiGetBlob). Функция DbiGetBlob (чтения данных из BLOB поля) позволяет прочитать любую часть BLOB поля. Для этого приложение указывает смещение относительно начала BLOB поля (параметр iOffSet) и длину интересующей порции данных (iLen). После выполнения функция DbiGetBlob возвращает количество прочитанных байт. Это число может быть меньше параметра iLen, если операция чтения достигла конца BLOB поля Версия IDAPI для Windows 3.1 не позволяет читать массивы данных длиннее чем 65К, поэтому большие BLOB поля придется считывать путем многократных вызовов функции DbiGetBlob. Для записи данных в BLOB поле приложение должно использовать функцию DbiPutBlob, которая эквивалентна функции DbiPutField для обычных полей. Функции DbiPutField необходимо передать указатель на блок данных, длину блока данных и смещение внутри BLOB поля, с которого начнется запись. Приложение имеет возможность как обновить уже существующую часть BLOB поля, так и добавить новую порцию данных в конец BLOB поля. После обновления данных в BLOB поле запись, содержащая данное BLOB поле, добавляется или вставляется в таблицу так же, как обычная запись. Как только необходимые изменения внесены в таблицу, приложение должно освободить BLOB поле вызовом DbiFreeBlob. Следующий пример иллюстрирует обработку BLOB полей: DBIResult rslt; pCHAR blobBuf; UINT32 blobSize, byteRead; //Читаем текущую запись DbiGetRecord(hCursor, dbiNOLOCK, pRecBuf, NULL); //Открываем BLOB поле rslt = DbiOpenBlob(hCursor, pRecBuf, 3, dbiREADWRITE); if(rslt == DBIERR_NONE) { //Узнаем размер BLOB поле и затем читаем его. //Предполагается что размер BLOB поле меньше 65К DbiGetBlobSize(hCursor, pRecBuf, 3, &blobSize); blobBuf = malloc(blobSize); DbiGetBlob(hCursor, pRecBuf, 3, 0, blobSize, (pBYTE) blobBuf, &byteRead); . . . // Освобождаем BLOB поле DbiFreeBlob(hCursor, pRecBufr, 3); // Освобождаем память free(blobBuf); } IDAPI поддерживает концепцию связанных курсоров, которая дает разработчику возможность связать две таблицы по-принципу "один-ко-многим". Между курсорами двух таблиц может быть установлена связь, если таблицы имеют общее поле, которое должно быть индексным в подчиненной таблице. Связывание двух курсоров заставляет курсор подчиненной таблицы видеть записи, содержащие значения ключа, совпадающее со значениями в текущей записи главной таблицы. Предположим, например, что главная таблица "Заказчики" и подчиненная таблица "Счета" имеют общее поле "Номер Заказчика". И если это поле в текущей записи главной таблицы равно, например 13, тогда курсор подчиненной таблицы будет видеть только записи, относящиеся к заказчику номер 13. Главная таблица может иметь несколько подчиненных таблиц. Но подчиненная таблица может связана только с одной главной. Одна подчиненная таблица может одновременно являться главной для другой подчиненной таблицы. IDAPI позволяет установить связь между таблицами любого типа (лишь бы существовал соответствующий драйвер). Для связывания двух таблиц приложение должно проделать следующие операции: Открыть курсоры двух таблиц. Курсор подчиненной таблицы должен иметь активный текущий индекс в поле, которое будет использоваться для связывания. Подготовить курсоры к связыванию. Для этого приложение вызывает функцию DbiBeginLinkMode для каждого курсора. Функция DbiBeginLinkMode возвращает новый курсор. Связать два курсора вызовом функции DbiLinkDetail. Типы связываемых полей в главной и в подчиненной таблицах должны совпадать. Транзакции могут выполняться на всех SQL платформах, которые поддерживает IDAPI. IDAPI позволяет только одной транзакции быть активной во время одной связи с SQL БД. Попытка начать вторую транзакцию до завершения первой обречена на ошибку. Все SQL операции IDAPI выполняет в контексте транзакции. Даже если приложение явно не выполняет транзакции, SQL драйвер управляет сервером через транзакции незаметно для приложения. Подчеркнем что транзакции по-умолчанию выполняются только с SQL серверами и только если приложение явно не использует транзакции. ЯВНОЕ ИСПОЛЬЗОВАНИЕ ТРАНЗАКЦИЙ Для явного использования транзакций, IDAPI предоставляет функции DbiBeginTran и DbiEndTran. Технология IDAPI предоставляет клиентским приложениям возможность использовать SQL или QBE запросы для доступа как к серверным БД так и к докальным таблицам dBASE и Paradox. IDAPI содержит группу функций обеспечивающих передачу SQL/QBE запросов. Для связи со стандартными БД (dBASE, Paradox), IDAPI предоставляет подмножество SQL. Это подмножество может использоваться для организации связи стандартных БД с серверными SQL таблицами (при этом должен быть установлен соответствующий SQL драйвер). Для связи только с SQL БД, IDAPI позволяет использовать родной диалект серверных систем, таких как Oracle или Sybase. QBE обеспечивает одинаковый доступ как к данным Paradox или dBase, так и к серверным БД. Для исполнения простых запросов приложение может использовать функцию DbiQExecDirect, которая не требует предварительных действий. Эта функция сразу выполняет запрос и возвращает идентификатор курсора, указывающего на полученный результат. При вызове функции DbiQExecDirect, приложение должно указать идентификатор курсора БД, язык запроса (SQL или QBE) и сам запрос. Следующий пример показывает, как выполнить SQL запрос с помощью функции DbiQExecDirect: DBIResult rslt; hDBICur hCur; bBYTE szQuery = "Select t.name, t.age" "from EMPLOYEE " "where t.age > 30 " "and t.salary > 1000000 "; rslt = DbiQExecDirect(hDB, qrylangSQL, szQuery, &hCur); Если приложению необходимо задать тип результирующей таблицы или задать параметры запроса, то лучше выполнить запрос по шагам. Выполнение запроса по шагам включает в себя: Подготовку запроса и получение идентификатора запроса (DbiQPrepare) Задание параметров запроса (DbiSetProp) Выполнение подготовленного запроса (DbiQExec) Освобождение ресурсов (DbiQFree) Перед выполнением запроса, приложение должно указать, какой тип результата желателен. Результат запроса может быть либо копией оригинальных данных, либо живыми данными. Но даже если приложение указывает живой тип данных, IDAPI не гарантирует получение живого результата. Для того чтобы убедиться в том, какой тип результата вернул запрос, необходимо получить свойство запроса bTempTable (с помощью функции DbiGetCursorProps). ЗАКЛЮЧЕНИЕ Тесные рамки статьи не позволяют показать все возможности новой
технологии. Но о широте и гибкости IDAPI говорит тот факт, что такие
популярные системы доступа к БД как, Paradox, dBase, Delphi, опираются
на эту технологию. Материал подготовлен в Демо-центре клиент-серверных технологий компании Borland на основе технической документации. Телефон для справок: (095)-535-0319 факс/тел.:535-5349 E-mail: thomas@bor.compnet.ru
|