Russian Belarusian English German Japanese Ukrainian

dbExpress

Механизм доступа dbExpress подразумевает использование совокупности драйверов, компонентов, инкапсулирующих соединения, транзакций, запросов, наборов данных и интерфейсов, с помощью которых обеспечивается универсальный доступ к функциям этого механизма. Обеспечение взаимодействия с серверами баз данных по технологии dbExpress основано на использовании специализированных драйверов. Последние для получения данных применяют запросы SQL. На стороне клиента при этом нет кэширования данных, здесь применяются только однонаправленные курсоры и не обеспечивается возможность прямого редактирования наборов данных.

Выполнение SQL-запроса

Для выполнения SQL-запроса на сервере БД служит компонент SQLQuery. Он позволяет представлять результаты выполнения запросов с помощью инструкции SELECT или осуществлять действия по изменению БД путем выполнения инструкций INSERT, DELETE, UPDATE, ALTER TABLE и т.п. Для компонента SQLQuery текст SQL-запроса определяется как значение свойства SQL типа TStrings. На этапе разработки приложения текст SQL-запроса может быть набран непосредственно в окне редактора строк (или редактора кода), открываемого двойным щелчком на свойстве SQL в окне Инспектора объектов. При выполнении приложения очистка содержимого свойства SQL компонента SQLQuery и его модификация могут быть выполнены, к примеру, с помощью следующего кода:
SQLQuery1->SQL->Clear();
SQLQuery1->SQL->Add("SELECT " + Edit1->Text + " FROM " + Edit2->Text);
if(!(Edit3->GetTextLen()== 0))
{
SQLQuery1->SQL->Add("ORDER BY " + Edit3->Text);
}
Свойство Text типа AnsiString в качестве значения содержит строковое представление SQL-запроса в виде одной строки, а не нескольких, как это может быть в случае свойства SQL. Свойство Active типа bool определяет, открыт набор данных или нет. Это свойство обычно используют, чтобы определить или установить заполнение набора данными. Открыть набор данных SQL-запроса можно также с помощью метода Open либо с помощью метода:
virtual int __fastcall ExecSQL(bool ExecDirect = false);
Значение true параметра ExecDirect означает, что запрос не имеет настраиваемых параметров.
 
Свойство DataSource связывает набор данных SQL-запроса с другим (главным) набором данных. Этот набор данных используется для получения параметров запроса в случае, когда предусматривается параметризованный запрос, но приложение этими параметрами не обеспечивает. Для примера приведем две процедуры обработки событий, в которых компонент SQLQuery1 используется для выполнения динамического SQL-запроса по отбору записей из таблицы CUSTOMER с целью просмотра содержимого полей 'CUST_NO' и 'PHONE_NO'. Таблица, принадлежит базе данных employee.gdb сервера InterBase.
void __fastcall TForm1::Button1Click{TObject *Sender)
{
Edit1->Text = "CUST_NO, PHONEJNO";
Edit2->Text="CUSTOMER";
Edit3->Text="CUST_NO";
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
try
{
SQLQuery1->Close();
SQLQuery1->SQL->Clear();
SQLQuery1->SQL->Add("SELECT " + Edit1->Text + " FROM " + Edit2- >Text);
if(!(Edit3->GetTextLen()== 0))
{
SQLQuery1->SQL->Add("ORDER BY " + Edit3->Text);
}
SQLQuery1->Open();
}
catch(...)
{
MessageDlg("Ошибка!", mtError, TMsgDlgButtons() << mbOK, 0);
}
Edit4->Text=SQLQuery1->FieldByName("CUST_NO")->AsString;
Edit5->Text=SQLQuery1->FieldByName("PHONE_NO")->AsString;
}
При обработке события Button1Click нажатия кнопки с заголовком Задать имена в компоненты Edit1, Edit2 и Edit3 заносятся имена отбираемых полей, имя таблицы и имя поля для сортировки. При обработке события Button2Click нажатия кнопки с заголовком Выполнить запрос указанные имена используются при формировании и выполнении SQL-запроса с помощью компонента SQLQuery1. Для просмотра значений отобранных полей с помощью SQL-запроса используются компоненты Edit4 и Edit5. Приведенный вариант кода обеспечивает просмотр полей первой записи набора данных. При необходимости перемещения по записям при просмотре можно воспользоваться методами First и Next.

Выполнение хранимых процедур

Для выполнения хранимых процедур, размещенных на сервере БД, служит компонент SQLStoredProc.
 
Имя хранимой процедуры задает свойство StoredProcName типа AnsiString. Для задания параметров хранимой процедуры предназначено свойство Params типа TParams. При обращении к параметрам хранимой процедуры целесообразно использовать метод ParamByName, это обусловлено тем, что при работе некоторыми серверами порядок следования параметров до и после выполнения процедуры может меняться. Для подготовки хранимой процедуры к выполнению на сервере служит метод:
TCustomSQLDataSet __fastcall PrepareStatement(int &RecordsAffected);
При его вызове сервером БД выделяются ресурсы и связываются их параметры. Поименованные параметры временно преобразуются к непоименованным, поскольку dbExpress поименованные параметры не поддерживает. Если хранимая процедура не возвращает набор данных, то ее запускают с помощью метода:
int __fastcall ExecProc(void);
В противном случае используется метод Open, либо свойству Active задают значение true.

Компонент редактирования набора данных

Для редактирования набора данных (получения данных, их кэширования и отправления измененных данных на сервер) предназначен компонент SQLClientDataSet. Этот компонент использует двунаправленный курсор и позволяет редактировать данные, но в режиме редактирования. Тем самым он исправляет основные недостатки рассматриваемой технологии. Для подготовки компонента SQLClientDataSet к работе с данными нужно с помощью свойства Connection связать его с компонентом соединения SQLConnection. В качестве альтернативы можно с помощью подсвойства ConnectionName свойства Connection задать тип соединения непосредственно. Произведенные над данными изменения размещаются в локальном кэше, в связи с этим для подтверждения изменений и отправки данных на сервер БД используют метод:
virtual int __fastcall ApplyUpdates(int MaxErrors);
Параметр MaxErrors определяет число ошибок, допустимых при передаче данных. Локальный кэш компонента после сохранения изменений на сервере можно очистить от данных с помощью метода:
bool __fastcall Reconcile(const System::OleVariant &Results);
Отмена локальных изменений данных может быть выполнена с помощью метода:
void __fastcall CancelUpdates(void);
Пересылка данных между сервером и компонентом SQLClientDataSet осуществляется с помощью пакетов. Размер пакета (по числу записей) можно задать с помощью свойства PacketRecords типа int. По умолчанию устанавливается значение -1, которое означает, что один пакет должен содержать все записи набора данных. Если значение свойства PacketRecords больше 0, то оно определяет число записей, которые можно получить в пакете от провайдера с помощью метода:
int __fastcall GetNextPacket(void);
Если значение свойства PacketRecords равно 0, то в пакете передаются только метаданные.
Свойство Datasize типа int устанавливает размер (в байтах) текущего пакета, доступного с помощью свойства Data типа OleVariant.
 
На общее число записей в источнике данных указывает свойство RecordCount типа int, номер текущей записи определяет свойство RecNo типа int. Изменения в текущей записи можно отменить с помощью метода:
void __fastcall RevertRecord(void);
Отменить последнюю операцию по изменению записи клиентского набора данных можно с помощью метода:
bool __fastcall UndoLastChange(bool FollowChange);
Здесь значение параметра определяет, где будет установлен курсор после восстановления записи: true - на восстановленной записи, false - на текущей записи. Обновить значение полей для текущей записи с сервера можно с помощью метода:
void __fastcall RefreshRecord(void);

Компоненты доступа к данным

Некоторые компоненты доступа к данным по технологии dbExpress (SQLDataSet, SQLQuery, SQLStoredProc и SQLTable) являются однонаправленными наборами данных, в которых отсутствует буферизация, и при этом на них накладываются заметные ограничения. В однонаправленных наборах данных используются однонаправленные курсоры. С их помощью допускается только получать данные, из методов навигации по набору данных поддерживаются лишь методы First и Next, подразумевающие последовательный перебор записей от начала к концу.
 
При использовании однонаправленных наборов данных отсутствует возможность прямого редактирования данных, так как для этого нужно размещение в буфере результатов редактирования. При этом соответствующее свойство названных компонентов CanModify всегда имеет значение false. Для однонаправленных наборов данных возможности редактирования данных все же доступны, например, путем задания оператора UPDATE языка SQL. Кроме того, для однонаправленных наборов данных по тем же причинам имеют место ограничения по выполнению фильтрации. Это ограничение также можно обойти путем указания параметров фильтрации в SQL-запросах.
 
Имеются также ограничения по отображению данных с помощью компонентов страницы Data Controls. В частности, нельзя использовать компоненты DBGrid и DBCtrlGrid, а также компоненты синхронного просмотра. Для компонента навигатора требуется отключить кнопки возврата и перехода на последнюю запись. Остальные компоненты можно использовать, как обычно.
 
Для компонента SQLClientDataset большинство из указанных ограничений не действует. Он использует внутренние компоненты типа TSQLDataSet и TDataset Provider для получения и изменения данных. Перед использованием любой из указанных компонентов доступа к данным следует подключить к серверу БД. Для этого достаточно его свойству SQLConnection в качестве значения установить имя компонента соединения SQLConection. Компоненты доступа к данным можно использовать, поместив в форме, либо динамически, создав при выполнении приложения.

Отладка соединения с сервером

Для получения информации о местах ошибок в SQL-запросах при отладке соединения приложения с сервером БД служит компонент SQLMonitor. Он осуществляет перехват сообщений между соединением и сервером баз данных и помещает их в список строк, определяемый свойством TraceList типа TStrings.
 
Для начала работы с компонентом нужно установить связь с соединением через свойство SQLConnection и активизировать его, задав свойству Active значение true.
Перед каждой записью сообщений в список строк возникает событие OnTrace, тип которого описан так:
typedef void __fastcall (__closure *TTraceEvent)(System::TObject* Sender, pSQLTRACEDesc CBInfo, bool &LogTrace);
а сразу после записи в список возникает событие onLogTrace типа:
typedef void __fastcall (__closure *TTraceLogEvent)(System::TObject* Sender, PSQLTRACEDesc CBInfo);
Содержимое списка можно сохранить в файле на жестком диске с помощью метода SaveToFile.
 
При задании свойству AutoSave типа bool значения true данные о проходящих командах автоматически заносятся в текстовый файл с именем, заданным значением свойства FileName типа Ansistring.
Свойство MaxTraceCount типа int определяет максимальное число контролируемых команд, а также управляет процессом контроля. При значении -1 ограничения снимаются, а при значении о контроль не выполняется. На число сохраненных в списке команд указывает свойство TraceCount типа int.
 
Пусть требуется выполнять оперативное отображение информации о последнем сообщении в окне многострочного редактора (компонент Memo1):
void __fastcall TForm1::SQLMonitorlLogTrace(TObject *Sender, pSQLTRACEDesc CBInfo)
{
Memo1->Lines->Clear();
Memo1->Lines=SQLMonitor1->TraceList;
SQLMonitor1->TraceList->Clear(); // Очистка списка сообщений
}
Как видно из приведенного кода, был использован обработчик события OnLogin. В качестве возможного варианта сообщения, отслеживаемого при работе компонента монитора, возможна строка вида:
INTERBASE - isc_dsql_free_statement
Как видно из приведенного текста, перехвачено сообщение от сервера INERBASE.
Для автономного dbExpress-приложения нужны две динамически подключаемые библиотеки (DLL). Первая библиотека содержит драйвер dbExpress, например, для сервера БД InterBase это dbexpint.dll. Вторая библиотека есть midas.dll, используемая для поддержки компонента SQLClientDataSet. Обе библиотеки могут быть откомпилированы вместе с проектом и включены в ехе-файл распространяемого приложения.

Просмотр таблиц

Для просмотра таблиц по технологии dbExpress служит компонент SQLTable. Он генерирует SQL-запрос для получения всех строк и полей указанной таблицы.
Имя таблицы определяется с помощью свойства TableName, и при подключении компонента к соединению его можно выбрать в комбинированном списке в окне Инспектора объектов.
 
Для получения табличного набора данных компонент SQLTable с помощью метода:
virtual void __fastcall PrepareStatement(void);
генерирует запрос на сервер БД.
 
Порядок следования данных в наборе определяется свойством IndexFieldNames типа AnsiString или IndexName типа AnsiString. Список индексов таблицы можно получить с помощью метода:
void __fastcall GetIndexNames(Classes::TStrings* List);
В качестве значения параметра List. Связь между двумя наборами данных «главный-подчиненный» устанавливается с помощью свойств MasterFields и MasterSource. На этапе разработки приложения двойным щелчком на свойстве MasterFields в окне Инспектора объектов можно открыть диалоговое окно редактора связей для визуального построения отношения «мастер-детальный».
 
Компонент SQLTable является однонаправленным курсором, тем не менее, он допускает удаление записей из таблицы на сервер БД с помощью метода DeleteRecords.

Универсальный доступ к данным

Для обеспечения универсального однонаправленного доступа к данным БД по технологии dbExpress служит компонент SQLDataSet. Он позволяет получать все записи из таблицы БД, производить выборку данных путем выполнения SQL-запросов или выполнять хранимые процедуры. Для определения типа выполняемых действий (с запросами, таблицами или хранимыми процедурами) свойству CommandType нужно установить соответственно одно из трех возможных значений:
  • сtQuery - в свойстве commandText указывается SQL-запрос
  • сtTable - в свойстве commandText указывается имя таблицы на сервере БД, при этом компонент автоматически генерирует SQL-запрос для получения всех записей для всех полей таблицы
  • ctstoredProc - в свойстве CommandText указывается имя хранимой процедуры
В случае выбора значения ctQuery текст SQL-запроса можно ввести в поле свойства CommandText с помощью Инспектора объектов или Редактора построения SQL-запроса CommandText Editor. В окне редактора в списке Tables доступны имена таблиц в базе данных, а в списке Fields имена полей выбранной таблицы, с помощью кнопок Add Table to SQL и Add Field to SQL можно добавить нужные таблицы и поля. При этом в поле SQL отображается автоматически формируемый SQL-запрос. После нажатия OK в окне редактора мы получаем нужное значение свойства CommandText. При выполнении приложения аналогичные действия для выполнения SQL-запроса можно задать так:
SQLDataSet1->CoinmandType = ctQuery;
SQLDataSet1->CornmandText = "select COUNTRY, CURRENCY from DEPARTMENT, DEPTJSTO, DEPARTMENT from COUNTRY";
SQLDataSet1->ExecSQL();
При выборе значения ctTable в свойстве CommandText указывается имя таблицы, например, так:
SQLDataSet1->CommandType = ctTable;
SQLDataSet1->CoinmandText = "COUNTRY";
SQLDataSet1->ExecSQL();
Для открытия набора данных нужно установить значение true свойству Active или использовать метод Open. Если же SQL-запрос или хранимая процедура не возвращают набор данных, для их выполнения используется метод:
virtual int __fastcall ExecSQL(bool ExecDirect = false);
Параметр ExecDirect определяет необходимость подготовки параметров перед выполнением инструкции. Если параметры запроса или процедуры существуют, параметр ExecDirect должен иметь значение false. Это означает, что их нужно подготовить. При необходимости сортировки данных используют инструкцию ORDER BY в случае варианта ctQuery, либо устанавливают с помощью редактора SortFields Editor значение свойства sortFieldNames в случае варианта ctTable. Для варианта ctStoredProc порядок сортировки определяется в хранимой процедуре.
 
При необходимости в SQL-запросе или в хранимой процедуре можно использовать параметры, позволяющие настраивать запрос или процедуру, не изменяя их код. Параметры задаются с помощью свойства params, которое является коллекцией объектов типа TParam. Свойство DataSource типа TDataSource компонента SQLDataSet позволяет связать два набора данных по схеме «мастер-детальный». Метод SetSchemaInfо компонента SQLDataSet позволяет получить метаданные (список таблиц на сервере, список системных таблиц, информация о хранимых процедурах, информация о полях таблицы, параметры хранимой процедуры).

Установление соединения с сервером

Для установления соединения с сервером базы данных служит компонент SQLConnection, который представляет собой аналог компонента DataBase в BDE.
Этот компонент взаимодействует с двумя файлами, расположенными в каталоге ..\Common Files\Borland Shared\DBExpress. Файл dbxdrivers.ini содержит список инсталлированных драйверов серверов БД и для каждого драйвера список динамически подключаемых библиотек и параметров соединений, установленных по умолчанию. Список соединений с параметрами соединений содержится в файле dbxconnections.ini. Для наглядности приведем фрагмент кода из этого файла:
[OrасleConnection]
DriverName=Orасle
DataBase=Database Name
User_Name=user
Password=password
RowsetSize=20
BlobSize=-1
ErrorResourceFile=
LocaleCode=0000
Oracle TransIsolation=ReadCommited
OS Authentication=False
Multiple Transaction=False
Trim Char=false
Поместив компонент SQLConnection в форму (или модуль данных) на этапе разработки приложения, можно выбрать одно из существующих соединений, либо создать новое соединение с помощью диалогового окна Редактора соединений. Вызов указанного окна можно выполнить выбором пункта Edit Connection Properties контекстного меню компонента.
 
Редактор соединений позволяет настроить существующее соединение dbExpress или создать новое соединение, а также проверить правильность настроек с помощью кнопки Test Connection. Параметры соединения также можно настраивать с помощью Инспектора объектов (свойство Params типа TStrings).
Параметры соединения для основных серверов баз данных приведены в таблице ниже.
Сервер БД Параметр Значение
Все серверы BlobSize Ограничение на объем пакета для данных типа BLOB
  DriverName Имя драйвера
  ErrorResourceFile Файл сообщений об ошибках
  LocaleCode Код локализации, определяющий влияние национальных символов на сортировку данных
  User_Name Имя пользователя
  Password Пароль
  <имя сервера БД> Translsolation Уровень изоляции транзакций для сервера с заданным именем
InterBase CommitRetain Поведение курсора по завершению транзакции: true - обновление; false - удаление
  RoleName Роль пользователя
  SQLDialect Используемый диалект SQL
  Trim Char Возможность удаления из строковых значений полей пробелов, дополняющих их до полной строки
  WaitOnLocks Разрешение на ожидание занятых ресурсов
  DataBase Имя файла базы данных (с расширением gdb)
DB2 DataBase Имя клиентского ядра
MySQL DataBase Имя базы данных
Microsoft SQL Server 2000
MySQL
Informix
HostName Имя компьютера, на котором установлен сервер
Microsoft SQL Server 2000
Informix
DataBase Псевдоним (алиас) имени базы данных
Microsoft SQL Server 2000
Oracle
OS Autenification Использование учетной записи текущего пользователя операционной системы при доступе к ресурсам сервера
Informix
Oracle
Trim Char Возможность удаления из строковых значений полей пробелов, дополняющих значение до полной строки
Oracle
AutoCommit Флаг завершения транзакции. Устанавливается сервером
  BlockingMode Режим завершения запроса, true - соединение дожидается окончания запроса; false - начинает выполнение следующего запроса
  Multiple Transaction Возможность управления несколькими транзакциями в одном сеансе
  DataBase Запись базы данных в файле TNSNames.ora
После описанной настройки параметров соединений для компонента SQLConnection с помощью Инспектора объектов можно выбрать соединение (свойство ConnectionName) для нужного сервера баз данных. При этом автоматически устанавливаются значения связанных с ним свойств:
  • DriverName (имя драйвера)
  • LibraryName (имя динамически подключаемой библиотеки Драйвера)
  • Params (параметры соединения)
  • VendorLib (имя динамически подключаемой библиотеки клиентской части сервера)
Открытие соединения с сервером БД осуществляется заданием свойству Connected типа bool значения true. Например, при выполнении приложения это можно сделать с помощью кода:
SQLConnection1->Connected = true;
Соответственно закрыть соединение с сервером можно путем задания этому свойству значения false. Названные операции могут быть выполнены также с помощью методов:
void __fastcall Open (void);
void __fastcall Close(void);
С помощью свойства LoginPrompt типа bool выполняется задание необходимости (true) или ненужности (false) отображения окна авторизации для ввода имени пользователя (User_Name) и пароля (Password) при каждой попытке соединения с сервером. В последнем случае эти значения будут браться из файла dbxconnections.ini.
 
После открытия соединения все компоненты dbExpress, инкапсулирующие наборы данных и связанные с открытым компонентом SQLConnection, получают доступ к базе данных.
При установлении соединения с сервером InterBase в качестве имени пользователя и пароля можно использовать строки Sysdba и masterkey.
При открытии и закрытии соединения можно воспользоваться любым из доступных событий, например:
__property Classes::TNotifyEvent AfterConnect = {read=FAfterConnect, write=FAfterConnect};
__property Classes::TNotifyEvent AfterDisconnect = {read=FAfterDisconnect, write=FAfterDisconnect};
__property Classes::TNotifyEvent BeforeConnect = {read=FBeforeConnect, write=FBeforeConnect};
__property Classes::TNotifyEvent BeforeDisconnect = {read=FBeforeDisconnect, write =FBeforeDisconnect};
property OnLogin: TSQLConnectionLoginEvent;
Например, событие BeforeConnect можно использовать для вызова функции, выполняющей дешифрование пароля доступа к базе данных. Соответствующий обработчик события может быть задан следующим образом:
void __fastcall TForm1::SQLConnectionBeforeConnect(TObject *Sender)
{
if (SQLConnection1->LoginPrompt == false)
{
SQLConnection1->Params->Values["User_Name"] = "SYSDBA";
SQLConnection1->Params->Values["Password"] = Decrypt(SQLConnection1->Params->Values["Password"]);
}
Событие OnLogin наступает в случае, если свойству LoginPrompt установлено значение true. Если это свойство имеет значение true, и нет обработки события OnLogin, то пользователю будет предложен стандартный диалог авторизации. Узнать о текущем состоянии соединения позволяет свойство:
__property TConnectionState ConnectionState = {read=FConnectionState, write=FConnectionState};
тип которого описан так:
enum TConnectionState {csStateClosed, csStateOpen, csStateConnecting, csStateExecuting, csStatePetching, csStateDisconnecting};
Указанные в определении типа значения соответствуют следующим состояниям соединения:
  • csStateClosed - закрыто
  • csStateOpen - открыто
  • csstateConnecting - установление соединения
  • csStateExecuting - ожидание исполнения переданного SQL-запроса
  • csstateFetching - получение данных с сервера
  • csStateDisconnecting - завершение соединения
Компонент SQLConnection позволяет выполнять SQL-запросы с помощью следующей функции:
int __fastcall Execute (const AnsiString SQL, TParams Params, void *Resuitset = NULL);
выполняет запрос, определенный значением константы SQL, с параметрами Params, используемыми в SQL-запросе. Используемые в запросе параметры должны иметь тот же порядок следования, что в params и в инструкции SQL. Если SQL-запрос не содержит параметров, то свойству Params требуется задать значение NULL. Если при выполнении SQL-запроса возвращается результат, то в параметр ResultSet помещается указатель на объект типа TCustomSQLDataSet, содержащий результат.
 
При отсутствии в запросе параметров и возвращения записей рекомендуется использовать функцию:
int __fastcall Execute (const AnsiString SQL, void *Resuitset);
которая возвращает значение о при успешном завершении запроса или код ошибки в противном случае.
 
Подобно своим аналогам в BDE компонент SQLConnection позволяет выполнять запуск, фиксацию и откат транзакций соответственно с Помощью методов: StartTransaction, Commit и Rollback.