TObject);
begin
if SIBfibEventAlerterl.Registered then
SIBfibEventAlerterl.UnRegisterEvents;
end;
Зададим свойство Tag у кнопок равным соответственно 1, 2 и 3, и напишем общий обработчик события OnClick для них:
procedure TForml.ButtonsClick(Sender: TObject);
begin
if not pFIBTransactionl.InTransaction then
pFIBTransactionl.StartTransaction;
pFIBStoredProcl.Params[0].Aslnteger := TButton(Sender).Tag;
try
pFIBStoredProcl.ExecProc;
pFIBTransactionl.Commit;
except
pFIBTransactionl.Rollback;
end;
end;
Смысл кода очевиден - мы задаем значение параметра процедуры, используя свойство Tag у кнопки, на которую нажал пользователь, выполняем процедуру при помощи метода ExecProc и либо подтверждаем транзакцию, либо отменяем ее в случае ошибки. Нам остается только написать обработчик события OnEventAlert компонента SIBfibEventAlerterclass="underline"
procedure TForml . SIBf ibEvent Alerter 1 Event Alert (Sender : TObject ;
EventName: String, EventCount: Integer);
begin
ShowMessage(EventName + ' : ' + IntToStr(EventCount));
end;
Параметр EventName возвращает название произошедшего события, a EventCount возвращает количество событий EventName. Значение параметра EventCount требует некоторых разъяснений. Итак, при работе с событиями важно помнить, что реально они вызываются только в случае подтверждения транзакции. Иными словами, если вы вызываете некоторое событие 'NEW_CUSTOMER' при вставке записей в таблицу, и вызываете Commit только после вставки 1000 записей, то обработчик OnEventAlert будет вызван только один раз, сразу после Commit, и значение параметра EventCount будет равно 1000. Очевидно, что событие произошло 1000 раз, но приложение получает нотификацию обо всех событиях только после подтверждения транзакции, в контексте которой они происходили.
Мы можем запустить наше приложение. При нажатии на любую из кнопок мы будем получать сообщение о произошедшем событии, причем, как уже было сказано, если наше приложение запущено несколько раз и даже на разных компьютерах, все копии приложения будут получать нотификацию о событиях.
"Низкоуровневая" работа с внутренним буфером TpFIBDataSet
TpFIBDataSet включает несколько специальных методов для работы с внутренним буфером, в котором хранятся записи. В общем-то, данные методы превращают TpFIBDataSet в аналог TClientDataSet, ориентированный на InterBase. Наиболее интересным примером использования данных методов будет, пожалуй, построение диалога выбора, состоящего из двух списков (рис. 2.75).
Рис 2.75. Использование локального кеша TpFIBDataSet для организации двойного списка выбора
В данном примере мы будем использовать базу данных EMPLOYEE.GDB, входящую в стандартную поставку InterBase и FIBPlus. Покажем запросы для SourceDS:
SelectSQL:
SELECT
CUS.CUST_NO,
CUS.CUSTOMER
FROM
CUSTOMER CUS
ORDER BY CUS.CUSTOMER
UpdateSQL:
UPDATE CUSTOMER SET
CUSTOMER = ?CUSTOMER
WHERE
CUST_NO = ?OLD_CUST_NO
InsertSQL:
INSERT INTO CUSTOMER(
CUST_NO,
CUSTOMER
)
VALUES(
?CUST_NO,
?CUSTOMER
)
DeleteSQL:
DELETE FROM CUSTOMER
WHERE
CUST_NO = ?OLD_CUST_NO
RefreshSQL:
SELECT
CUS.CUST_NO,
CUS.CUSTOMER
FROM
CUSTOMER CUS
WHERE
(
CUS.CUST_NO = ?OLD_CUST_NO
)
Те же самые запросы мы будем использовать в TargetDS, поскольку оба компонента должны обладать совместимой структурой полей. Откроем оба запроса сразу после создания формы:
procedure TDualListForm.FormCreate(Sender: TObject);
begin
SourceDS.Open;
TargetDS.CacheOpen;
end;
Если мы не указываем в коде явного открытия базы данных, то это означает, что свойство Connected > компонета TpFlBDatabase было задано в True в design-time.
Обратите внимание на то, что TargetDS мы активируем при помощи специального метода CacheOpen. Этот метод не выполняет запрос из SelectSQL, а только подготавливает внутренний буфер компонента в зависимости от запроса в SelectSQL. Очевидно, что после запуска приложения мы увидим записи в левом списке и пустую таблицу в правом. Теперь мы можем написать процедуру, которая позволит переносить записи из одного списка в другой:
procedure TDualListForm.MoveRec(Select: Boolean);
begin
if Select then begin
if (SourceDS.Active) and (not SourceDS.IsEmpty) then begin
TargetDS.CacheRefreshByArrMap(SourceDS, frklnsert,
['CUST_NO'], ['CUST_NO']);
SourceDS.CacheDelete;
end;
end
else begin
if (TargetDS.Active) and (not TargetDS.IsEmpty) then begin
SourceDS.CacheRefresh(TargetDS, frklnsert, nil);
TargetDS.CacheDelete;
end;
end;
end;
Если параметр Select равен True, то мы будем переносить текущую запись из левого списка в правый, т. е. из SourceDS в TargetDS. Рассмотрим подробнее метод CacheRefreshByArrMap.
procedure CacheRefreshByArrMap(FromDataSet: TDataSet; Kind:
TCachRefreshKind; const SourceFields, DestFields: array of
String);
Метод позволяет вставлять или модифицировать текущую запись в локальный буфер компонента, т. е. без выполнения соответствующих модифицирующих запросов. Параметр FromDataSet указывает, из какого TpFIBDataSet мы хотим получить запись. Параметр Kind может принимать два значения: frkEdit или frklnsert. Frklnsert указывает, что мы хотим вставить новую запись, a frkEdit - что мы хотим изменить существующую. Параметр SourceFields описывает список полей из компонента источника, а список DestFields - список полей в TpFIBDataSet-приемнике. Формально названия полей в списках могут не совпадать, но необходимо, чтобы совпадали типы данных, иначе обмен данными будет невозможен. В нашем примере, как вы можете видеть, мы вставляем в TargetDS текущую запись из SourceDS. Данный метод использован здесь только для демонстрации, поскольку мы вполне могли бы обойтись методом CacheRefresh, который копирует соответствующие поля автоматически. После вставки записи необходимо удалить ее из компонента источника. Это делается при помощи метода CacheDelete, который также не выполняет соответствующий запрос (DeleteSQL), а удаляет запись только из локального буфера в памяти. Теперь, когда мы знаем, что делают процедуры CacheRefreshByArrMap, CacheRefresh и CacheDelete, код процедуры достаточно очевиден: мы копируем запись из компонента-источника в компонент-приемник и удаляем ее из компонента-источника.
То же самое можно сделать в цикле, если мы хотим перенести все записи сразу:
procedure TDualListForm.MoveAll(Select: Boolean);
var TmpDataSet: TpFIBDataSet;
begin
if Select then TmpDataSet := SourceDS else
TmpDataSet := TargetDS;
with TmpDataSet do begin
try
DisableControls;
First;
while not Eof do MoveRec(Select);
finally
EnableControls;
end;
end;
end;
Остается написать обработчики события OnClick для кнопок:
procedure TDualListForm.ButtonlClick(Sender: TObject);
begin
MoveRec(True); // SourceDS -> TargetDS
end;
procedure TDualListForm.Button2Click(Sender: TObject);
begin
MoveRec(False); // TargetDS -> SourceDS
end;
procedure TDualListForm.Button3Click(Sender: TObnect);
begin
MoveAll(True), // SourceDS -> TargetDS
end;
procedure TDualListForm. Button4Click (Sender : TObject) ;
begin
MoveAll(False); // TargetDS -> SourceDS
end;
Мы можем запустить наше приложение (рис 2.76)
Если мы будем нажимать на кнопку Button 1, то записи будут переноситься из левого списка в правый (рис. 2 77).
Все операции осуществляются только с данными внутри локальных буферов, не затражвают сервер и реальные данные в базе Наряду с описанными выше методами вы также можете использовать.