Интерфейс прикладного программирования
ВDЕ
Как уже говорилось выше, любое приложение Delphi, работающее с базами данных и написанное с использованием стандартных компонентов доступа к данным, обращается к данным и получает результат при помощи BDE. При этом механизм доступа к данным использует вызовы функций из API BDE.
Достаточно сложно представить себе такую ситуацию, когда возникает необходимость создания приложения, использующего только функции BDE, без применения компонентов доступа к данным VCL. А вот отдельные функции вполне могут понадобиться в любой программе. Поэтому рассмотрим процесс работы приложения, использующего вызовы BDE, т. к. это дает хорошую возможность понять механизм доступа к данным, который реализован в Delphi.
Итак, для создания приложения на основе вызовов функций BDE необходимо выполнить следующие операции:
1. Инициализация BDE (функция DbiInit).
2. Открытие объекта базы данных (функция DbiOpenDatabase).
3. Определение рабочего каталога (функция obiSetDirectory), если на предыдущем этапе не задается псевдоним БД.
4. Определение временного каталога (функция DbiSetPrivateoir).
5. Открытие набора данных и создание курсора (функции DbOреnТаblе, DbiQExec и пр.; дескриптор курсора hDBICur).
6. Заполнение структуры cuRProps, содержащей данные о курсоре и наборе данных (функция DbiGetCursorProps).
7. Выделение памяти для буфера записи.
8. Навигация набору данных (функции DbiSetToBegin, DbiSetToEnd, DbiSetToCursor и пр.)
9. Чтение необходимой записи (функции DbiGetRelativeRecord, DbiGetNextRecord, DbiGetRecord, DbiGetPriorRecord и др.).
10. Чтение или обновление необходимого поля (функции DMGetFieid, DbiPutField).
11. Освобождение всех ресурсов (освобождение буфера записи, закрытие курсора, таблицы, BDE).
При использовании в программе функций из API BDE необходимо включить в секцию uses модуль BDE.
В прикладном программировании задачи, которые требовали бы выполнения всех описанных выше операций, практически не встречаются. Между тем, включение в программный код отдельных функций API BDE оправдано. В качестве примера рассмотрим приложение, которое позволяет очистить таблицу базы данных (рис. 16.4).
Дело в том, что при удалении записи из таблицы локальной СУБД (например Paradox) размер файла таблицы остается прежним, даже если удалить все записи. То есть на самом деле запись не удаляется, а только становится недоступной. При интенсивном использовании базы данных файлы таблиц могут занимать значительные объемы дискового пространства при довольно умеренном числе записей.
Рис. 16.4. Главная форма проекта BDEEmptyTable
Полная очистка таблиц базы данных осуществляется функцией DbiErr.ptyTable из API BDE. Именно она используется в демонстрационном приложении для радикального уменьшения размера таблиц.
Примечание
Функция DbiEmptyTable используется в методе EmptyTable компонентов доступа к данным (см. гл. 17).
В листинге 16.1 приведен исходный код этого приложения. Помимо указанной функции, в нем используются функции создания списка параметров доступных баз данных и таблиц текущей базы данных.
Листинг 16.1. Модуль главной формы приложения BDEEmptyTable
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, BDE, ExtCtrls, DBCtrls, Grids, DBGrids, Db, DBTables, Buttons;
type
TMainForm = class(TForm)
AliasesList: TComboBox;
TablesList: TComboBox;
EmptyBtn: TBitBtn;
Labell: TLabel;
Label2: TLabel;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormShow(Sender: TObject);
procedure AliasesListChange(Sender: TObject);
procedure EmptyBtnClick(Sender: TObject); private
hDB: hDBIDB;
hCursor: hDBICur;
DBDesc: DBDesc;
TblDesc : TBLBaseDesc;
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.FormShow(Sender: TObject);
var Rslt: DBIResult;
begin
AliasesList.Items.Clear;
TablesList.Items.Clear; hDB := Nil;
try
DbiInit(Nil); // Инициалы BDE
BDE DbiOpenDatabaseList(hCursor) ;
repeat
Rslt:= DbiGetNextRecord(hCursor, dbiNOLOCK, @DBDesc, nil) ;
if (Rslt 0 DBIERR_EOF) then
AliasesList.Items.Add(StrPas(DBDesc.szName));
until (rslt <> DBIERR_NONE); DbiCloseCursor(hCursor) ;
except
on E:EDBEngineError do ShowMessage ('OiiMSKa MHMunajiM3auMM BDE');
end;
end;
procedure TMainForm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
try
finally
if hDB <> Nil then DbiCloseDatabase(hDB);// Закрытие базы данных
DbiExit; // Закрытие сеанса работы с ВОЕ
end
end;
procedure TMainForm.AliasesListChange(Sender: TObject);
begin
try
if hDB <> Nil
then DbiCloseDatabase(hDB);// Закрытие базы данных
DbiOpenDatabase // Открытие базы данных
(
PChar(AliasesList.Text), // Псевдоним базы данных
Nil, // Тип базы данных
dbiReadWrite, // Режим редактирования данных
dbiOpenShared, // Режим разделения данных
Nil, // Пароль
0, // Число дополнительных параметров
Nil, // Перечень полей для доп. параметров Nil,
// Список доп. параметров hDB
// Дескриптор базы данных
);
DbiSetPrivateDir('с:\temp');// Определение временного каталога
DbiOpenTableList(hDb, False, False, '*.DB', hCursor);
TablesList.Items.Clear;
TablesList.Clear;
while DbiGetNextRecord(hCursor, dbiNOLOCK, @TblDesc, nil) = dbiErr_None
do TablesList.Items.Add(TblDesc.szName);
DbiCloseCursor(hCursor);
except
on E:EDBEngineError do ShowMessage('Ошибка открытия базы данных');
end;
end;
procedure TMainForm.EmptyBtnClick(Sender: TObject);
begin
try
DbiEmptyTable(hDB, Nil, PChar(TablesList.Text), '');
except
on E:EDBEngineError do ShowMessage('Неверно задана таблица'};
end;
end;
end.
При открытии главной формы (метод-обработчик FormShow) функция Dbiinit осуществляет инициализацию BDE. Затем функция DbiOpenDatabaseList создает в памяти временную таблицу, в которую записываются характеристики каждой зарегистрированной базы данных. Для этого применяется структура DBDesc. Курсор hcursor обеспечивает доступ к записям о базах данных.
После этого функция DbiGetNextRecord позволяет осуществить последовательное считывание имен псевдонимов баз данных (для этого в параметре передается указатель на структуру DBDesс) и их запись в список компонента AliasesList типа TComboBox.
При выборе из этого списка конкретного псевдонима работает метод-обработчик AliasesListchange. В нем открывается соответствующая база данных (функция DbiOpenDatabase), доступ к которой в дальнейшем осуществляется через дескриптор hDB.
Функция DbiopenTableList создает временную таблицу в памяти, в которую помещаются данные о таблицах выбранной базы данных в соответствии с форматом структуры TBLBaseDesс. Функция DbiGetNextRecord позволяет передать эту информацию в список компонента TablesList типа TCombоВох.
При щелчке на кнопке EmptyBtn в методе-обработчике EmptyBtndick работает функция DbiEmptyTabie, которая очищает выбранную ранее в компоненте TablesList таблицу.
Теперь рассмотрим пример простейшего приложения, которое может отображать два поля из таблицы COUNTRY. DВ в демонстрационной базе данных DBDEMOS. Эта база данных поставляется в комплекте Delphi. В примере использованы только функции API BDE.
Проект называется DirectBDE и имеет только одну форму, в которой отображаются сведения из таблицы COUNTRY. DB о государствах и их столицах (рис. 16.5). Кнопки в нижней части формы позволяют перемещаться по набору данных.
Листинг 16.2. Модуль главной формы приложения DirectBDE
unit Unitl;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, BDE, ExtCtrls;
type
TMainForm = class(TForm)
PriorBtn: TBitBtn;
Panel2: ТPanel;:
NextBtn: TBitBtn;
Label3: ТLabel;
CountryEdit: TEdit;
Label1: TLabel;
CapitalEdit: TEdit;
procedure PriorBtnClick(Sender: TObject);
procedure FormShow(Sender: TObject};
procedure FormClose(Sender: TObject;
var Action: TCloseAction);
procedure NextBtnClick(Sender: TObject);
private
hDB: hDBIDB;
hCur: hDBICur;
CursProps: CurProps;
RecBuf: pByte;
FValue: array [0 .. 255] of Char;
IsEmpty: Bool;
procedure OnBDEError;
public
end;
var
MainForm: TMainForm;
implementation ($R *.DFM}
procedure TMainForm.OnBDEError;
var ErrInfo: dbiErrlnfo; // Структура, содержащая информацию об ошибках
AStr: String;
begin
DbiGetErrorlnfo(True, Errlnfo);// Функция возвращает информацию об ошибке c
ase Errlnfo.iError of
9733: AStr := 'Для создания записи недостаточно параметров';
10024: AStr := 'Ошибка доступа к данным';
10245: AStr := 'База данных занята другим пользователем';
10038: AStr := 'Значение поля задано неверно';
11871: AStr := 'Несоответствие типов';
11959: AStr := 'В выражении отсутствует оператор GROUP BY';
else
AStr := 'Ошибочная операция с данными';
end;
ShowMessage(AStr) ;
end;
procedure TMainForm.FormShow(Sender: TObject);
begin
hDB := Nil; hCur := Nil;
Dbilnit(Nil); // Инициализация системы
BDE DbiOpenDatabase // Открытие базы данных
(
'DBDEMOS', // Псевдоним базы данных
Nil, // Тип базы данных
dbiReadWrite, // Режим редактирования данных
dbiOpenShared, // Режим разделения данных
Nil, // Пароль
0, // Число дополнительных параметров
Nil, // Перечень полей для доп. параметров
Nil, // Список доп. параметров
hDB // Дескриптор базы данных
);
DbiSetPrivateDir('с:\temp'); // Определение временного каталога
DbiOpenTable // Открытие таблицы
(
hDB, // Дескриптор базы данных
PChar('COUNTRY'), // Название таблицы
PChar(szParadox), // Тип таблицы (только для локальных БД)
Nil, // Название индекса (необязательный)
Nil, // IndexTagName — только для dBASE
0, // 0 — использовать первичный индекс
dbiReadWrite, // Режим редактирования данных
dbiOpenShared, // Режим разделения данных
xltField, // Режим трансляции данных
False, // Признак одностороннего перемещения курсора
Nil, // Дополнительные параметры
hCur // Дескриптор курсора таблицы );
DbiGetCursorProps // Определение параметров курсора
(
hCur, // Дескриптор курсора таблицы
CursProps // Структура параметров курсора
);
GetMem // Вьщеление памяти под буфер записи
(
RecBuf,
CursProps.iRecbufSize*SizeOf(Byte) );
DbiSetToBegin(hCur); // Установка курсора в начало набора данных
DbiGetNextRecord // Перемещение на первую запись
(
hCur, // Дескриптор курсора таблицы
dbiNoLock, // Режим ограничения доступа
RecBuf, // Буфер записи
Nil // Параметры записи );
DbiGetField // Получение значения поля
(
hCur, // Дескриптор курсора таблицы
1, // Номер поля в структуре таблицы
RecBuf, // Буфер записи
@FValue, // Переменная, в которую передается значение
IsEmpty // Признак пустой ячейки );
MainForm.CountryEdit.Text := FValue;
DbiGetField(hCur, 2, RecBuf, @FValue, IsEmpty);
MainForm.CapitalEdit.Text := FValue;
end;
procedure TMainForm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin try
finally
FreeMem(RecBuf); // Освобождение памяти буфера записи DbiCloseCursor(hCur); // Закрытие курсора
DbiCloseDatabase(hDB); // Закрытие базы данных
DbiExit; // Закрытие сеанса работы с ВОЕ
end
end;
procedure TMainForm.PriorBtnClick(Sender: TObject);
begin
try
if DbiGetPriorRecord(hCur, dbiNoLock, RecBuf, Nil) = DBIERR_BOF
then PriorBtn.Enabled := False
else
begin
if Not NextBtn.Enabled then NextBtn.Enabled := True;
DbiGetField{hCur, 1, RecBuf, SFValue, IsEmpty);
MainForm.CountryEdit.Text := FValue;
DbiGetField(hCur, 2, RecBuf, @FValue, IsEmpty);
MainForm.CapitalEdit.Text := FValue;
end;
except
OnBDEError;
end;
end;
procedure TMainForm.NextBtnClick(Sender: TObject);
begin
try
if DbiGetNextRecord(hCur, dbiNoLock, RecBuf, Nil)=DBIERR_EOF
then NextBtn.Enabled := False
else
begin
if Not PriorBtn.Enabled then PriorBtn.Enabled := True;
DbiGetFieldfhCur, 1, RecBuf, @FValue, IsEmpty);
MainForm.CountryEdit.Text := FValue;
DbiGetField(hCur, 2, RecBuf, @FValue, IsEmpty);
MainForm.CapitalEdit.Text := FValue;
end;
except
OnBDEError;
end;
end;
end.
Рис. 16.5. Главная форма проекта DirectBDE
При показе главной формы приложения в процедуре Formshow проводится инициализация BDE, открытие базы данных и таблицы. При этом создаются дескрипторы базы данных hDB и курсора таблицы hour, которые играют в дальнейшей работе приложения важную роль. Если при создании базы данных не указывать псевдоним БД, то обязательно нужно определить рабочий каталог базы данных с помощью функции DbiSetoirectory.
После этого отводится память под буфер записи, в который будут передаваться значения полей текущей строки таблицы. Размер буфера определяется при помощи Структуры CURPropS.
Затем курсор устанавливается на начало набора данных и на первую запись и осуществляется чтение значений двух полей таблицы.
Навигация по набору данных реализована в методах-обработчиках на нажатие кнопок формы. Их действие аналогично за исключением направления перемещения. При щелчке на кнопке выполняется переход на следующую или предыдущую запись, данные из новой записи помещаются в буфер записи RecBuf. Оттуда при помощи функции DMGetFieid осуществляется чтение значений полей. При достижении начала или конца набора данных кнопка деактивируется.
При закрытии формы проводятся операции по освобождению памяти буфера записи, закрытию базы данных и BDE.