...
...

Секреты Delphi. Использование TClientDataset в двухуровневых приложениях. Часть 3

Секреты Delphi. Использование TClientDataset в двухуровневых приложениях. Часть 3

Наконец настало время первых опытов. К сожалению, не удалось найти оригинальный код демонстрационных компонент Дана Мишера, так что придется воспользоваться созданной несколько лет назад на их базе компонентой TBS_ClientDataset.

Найти ее можно по адресу http://berdachuk.at.tut.by/downloads/bs.zip (эта библиотека компонент бесплатна и содержит исходные коды). Для данного примера также воспользуемся еще одной компонентой TBS_ADODataset названной библиотеки. TBS_ClientDataset для Delphi 5 является наследником стандартной компоненты TClientDataset.

Создадим простой проект в Delphi, содержащий одну форму frmMain и два модуля данных dmClient и dmRemote. Развивая тему использования сервера БД Oracle 9i Lite, воспользуемся демонстрационной базой данных POLITE. При отсутствии Oracle 9i Lite можно воспользоваться демонстрационной базой данных \Borland Shared\Data\DBDEMOS.mdb, входящей в пакет поставки Delphi.
Наиболее простым способом соединения с базой данных в этом случае будет использование компонент ADO. Для соединения с сервером в модуль dmRemote добавим компоненту TADOConnection. При стандартной установке сервера Oracle 9i Lite алиас ODBC-соединения POLITE должен быть прописан автоматически. Если для установки использовался клиентский CD, то придется создать его самостоятельно. Для настройки соединения нажмем кнопку "…" свойства ConnectionString и выберем режим использования строки соединения см. рис. 1.

Информацию по дальнейшей настройке можно найти в статье "Секреты Delphi. Соединение с базами данных при помощи компонент ADO" (КГ №2/2004). Добавим в этот же модуль компоненту TBS_ADODataset палитры компонент BS (назовем ее rdsDept). Свяжем свойство Connection с компонентой TADO_Connection. Для настройки SQL Текста нажмем кнопку "…" свойства CommandText и в появившемся SQL-редакторе введем запрос к таблице отделов см. рис. 2.

Основное отличие компоненты TBS_ADO Dataset от TADODataset, наследником которой она является, — наличие дополнительного свойства UniqueFields. В данное свойство через точку с запятой можно прописать список полей, которые будут уникально идентифицировать текущую запись. Это требуется для обеспечения возможности редактирования традиционно ReadOnly наборов данных, а также в случаях, если первичные ключи для выбранного сервера БД определяются некорректно. Если данное свойство не заполнять, то будет вызываться стандартная функция PSGetKeyFields интерфейса IProviderSupport компоненты TADODataset.
Добавляем в модуль dmRemote компоненту TDatasetProvider палитры компонент midas и связываем ее свойство Dataset с добавленной ранее компонентой rdsDept. Получим следующую картину (см. рис. 3).

Свойство ResolveToDataset устанавливаем в значение false, что позволит передать управление процессом подтверждения изменений встроенной компоненте TSQLResolver, которая передает изменения сразу компоненте, которая связана с сервером базы данных — в нашем случае это dbConnection. Это позволяет исключить необходимость использования дополнительных компонент (например, TUpdateSQL), скрипты InsertSQL, DeleteSQL и UpdateSQL будут формироваться автоматически.
Свойство UpdateMode устанавливаем в значение upWhereKeyOnly. При наличии первичных ключей это существенно повысит скорость поиска записи сервером базы данных во время внесения изменений.
Применительно к серверу Oracle, если требуется указание схемы, что желательно для разграничения доступа, надо будет реализовать метод, возвращающий имя редактируемой таблицы события OnGetTableName. Внутренняя реализация данного метода при указании имени схемы перед именем таблицы возвращает неверный результат. Этот же метод потребуется для обеспечения возможности редактирования ReadOnly наборов данных.

procedure TdmRemote.dspDeptGetTableName(Sender: TObject; DataSet: TDataSet;
var TableName: String);
begin
TableName:='System.Dept';
end;

Теперь настало время клиентского модуля данных. Добавим в него компоненту TBS_ ClientDataSet. В родительском компоненте TClientDataset используется свойство Provider Name для связи с провайдером TDatasetProvider. Но при размещении компонент связки TClientDataset и TdatasetProvider в различных модулях программы список провайдеров становится недоступен. Для решения данной проблемы в TBS_ClientDataset добавлено свойство DataProvider. Устанавливаем данное свойство в dmRemote.dspDept. Реализация метода SetData Provider позаимствована из статьи Дана Мишера "Using the MIDAS ClientDataSet as a Replacement for Cached Updates".
Теперь появляется возможность открывать полученный набор данных. Добавим в этот же модуль компоненту TDataSource, позволяющую связывать наш TClientDataset с элементами управления. Для защиты от случайных изменений данных пользователями желательно установить значение свойства AutoEdit в false. В результате этих манипуляций клиентский модуль данных будет иметь вид, как на рис. 4.

Начиная с данного момента данные уже можно редактировать, но на сервер результаты сделанных изменений посылаться не будут. Реализация метода отсылки данных на сервер определяется принятой схемой работы. В простейшем случае можно производить эту операцию сразу после фиксации данных методом post, а также удаления данных. В нашем примере реализуем следующие методы:

procedure TdmClient.cdsDeptApplyUpdates;
begin
if cdsDept.ChangeCount > 0 then
cdsDept.ApplyUpdates(-1);
end;

procedure TdmClient.cdsDeptAfterDelete(DataSet: TDataSet);
begin
cdsDeptApplyUpdates();
end;

procedure TdmClient.cdsDeptAfterPost(DataSet: TDataSet);
begin
cdsDeptApplyUpdates();
end;

procedure TdmClient.cdsDeptBeforeClose(DataSet: TDataSet);
begin
if (cdsDept.State in [dsInsert,dsEdit]) then
cdsDept.Post;
end;

В главной форме для примера разместим кнопку открытия, DBGrid и DBNavigator. Пример работы программы показан на рис. 5.

Для исключения проблем с освобождением ресурсов желательно принудительно закрывать все открытые экземпляры TClientDataset перед завершением работы программы.

procedure TdmClient.DataModuleDestroy(Sender: TObject);
var
i:integer;
begin
for i:=0 to Self.ComponentCount-1 do
if (self.Components[i] is TBS_ClientDataSet) and
(TBS_ClientDataSet(self.Components[i]).Active) then
begin
TBS_ClientDataSet(self.Components[i]).Close;
end;
end;

Пример созданного проекта можно скачать по адресу http://berdachuk.at.tut.by/downloads/ midasdemo1.zip. В приведенном архиве содержится также версия демонстрационного проекта для базы данных Microsoft Access DBDEMOS.mdb.

Продолжение следует.

Сергей Бердачук, Berdachuk@tut.by
http://berdachuk.at.tut.by



© Компьютерная газета

полезные ссылки
IP камеры видеонаблюдения