Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Мир InterBase. Архитектура, администрирование и разработка приложений баз данных в InterBase/FireBird/Yaffil - А Ковязин

Мир InterBase. Архитектура, администрирование и разработка приложений баз данных в InterBase/FireBird/Yaffil - А Ковязин

Читать онлайн Мир InterBase. Архитектура, администрирование и разработка приложений баз данных в InterBase/FireBird/Yaffil - А Ковязин

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 41 42 43 44 45 46 47 48 49 ... 112
Перейти на страницу:

v[2] = FieldByName( LQ2 ) AsString,

QryArrField Fields[0] SetArrayValue(v);

QUpdate Params ByName[ LQ ] AsQuad :=

QryArrField.Fields[0] AsQuad;

end;

QryArrField Close;

end;

Теперь все становится совершенно очевидным Поскотьку свойство QiyAnField KindUpdate равно ukModify, то QiyArrField выполняет запрос при изменении записи в AnayDataSet а поскольку свойство QiyArrField ExecuteOider равно eoBeforeDefault, то запрос (QiyArrField SQL) выполняется до того, как AirayDataSet выполнит свой собственный UpdateSQL В обработчике QryArrField AfterExecute мы всего лишь получаем заново все текущие элементы массива из базы данных, подменяем два из них новыми значениями, которые указал пользователь, и задаем значение параметра для ArrayDataSet.UpdateSQL Это означает что при выполнении ArrayDataSet.UpdateSQL формально будут обновлены все пять элементов массива но фактически изменены значения тотько двух элементов, которые изменил пользователь в TDBGrid.

Как видите работа с массивами достаточно проста поскотьку все основные сложности решают компоненты FIBPlus Хотелось бы также рассмотреть еще один специализированный компонент, входящий в FIBPlus Пример DemoArray демонстрирует работу с TDataSetContamei, использованным для синхронизации значений двух TpFIBDataSet, редактирующих наше апау-поле (рис 2 64)

Рис 2.64. Использование TDatabetContamer

Компонент DataSetContamerl помещен вместе с Database и Transaction на DataModule в нашем приложении Оба компонента AnayDataSet из разных форм нашего приложения ссылаются на DataSetContamerl при помощи свойства Contamei (рис 2 65)

Рис 2.65. Подключение компонентов TpFIBDataSet к DataSetContamerl

Компонент TDataSetContamei позволяет централизованно обрабатывать события от разных компонентов TpFIBDataSet, а также (расширяя, таким образом, список стандартных событий) посылать им сообщения, при получении которых они могут производить какие-то дополнительные действия. В нашем примере DataSetContainerl имеет обработчики двух событий OnDataSetEvent и OnUserEvent

procedure TDataModule2.DataSetsContainerlDataSetEvent(DataSet:

TDataSet;

Event: TKindDataSetEvent);

var Info: string;

begin

if Event = deAfterPost then

if DataSet.Owner.Name = 'Forml' then

DataSetsContainerl.NotifyDataSets(DataSet,

'Form2.ArrayDataSet', 'JOB_TABLE_CHANGED', Info)

else

if DataSet.Owner.Name = 'Form2' then

DataSetsContainerl.NotifyDataSets(DataSet,

'Form1.ArrayDataSet', 'JOB_TABLE_CHANGED', Info);

end;

procedure TDataModule2.DataSetsContainerlUserEvent(Sender:

TObject;

Receiver: TDataSet; const EventName: String; var Info:

String) ;

begin

if EventName = 'JOB_TABLE_CHANGED' then begin

with TpFIBDataSet(Sender) do

if (not CachedUpdates) and

(not TpFIBDataSet(Receiver).CachedUpdates) then

if

TpFIBDataSet(Receiver).Locate('JOB_CODE;JOB_GRADE;JOB_COUNTRY', varArrayOf([

FieldByNamet'JOB_CODE').AsString,

FieldByName('JOB_GRADE').AsString,

FieldByName('JOB_COUNTRY').AsString

]), []) then TpFIBDataSet(Receiver) Refresh

end;

end;

Смысл действий сводится к следующему: после изменения записи в одном из наших компонентов ArrayDataSet происходит событие AfterPost. Поскольку DataSetContainerl перехватывает все события у подчиненных компонентов, то срабатывает обработчик OnDataSetEvent. Параметр Event равен deAfterPost, а параметр DataSet ссылается на тот компонент, в котором произошло изменение записи. При помощи вызова метода NotifyDataSets DataSetContainerl посылает сообщение оставшемуся компоненту ArrayDataSet о том, что произошло изменение записи. Поскольку оба компонента на самом деле редактируют одну и ту же таблицу, то желательно синхронизировать изменения. Синхронизация происходит в обработчике события OnUserEvent, то есть при получении "извещения" об изменении какого-либо из ArrayDataSet.

Если получено сообщение "JOB_TABLE_CHANGED" и ни в одном из наших двух ArrayDataSet не включен режим CachedUpdates (в этом случае Refresh просто ничего не даст), то мы позиционируемся на соответствующую запись и вызываем для нее метод Refresh, обновив, таким образом, запись в одном ArrayDataSet после того, как она была изменена в другом ArrayDataSet.

Данная технология является совершенно уникальной: использование DataSetContainer позволяет делать чрезвычайно гибкие и в то же время прозрачные схемы обработки и синхронизации данных в нескольких компонентах TpFIBDataSet.

Работа с BLOB-полями

Достаточно часто желательно хранить в базе данных разнообразные неструктурированные данные: изображения, OLE-объекты, звук и т. д. Специально для этих целей существует специальный тип данных - BLOB Продемонстрируем использование BLOB-полей на примере простого приложения (см. рис. 2.66), использующего следующую таблицу:

CREATE TABLE BIOLIFE (

ID INTEGER NOT NULL,

CATEGORY VARCKAR (15) character set WIN1251 collate

WIN1251,

COMMON_NAME VARCHAR (30) character set WIN1251 collate

WIN1251,

SPECIES_NAME VARCHAR (40) character set WIN1251 collate

WIN1251,

LENGTH_CM_ DOUBLE PRECISION,

LENGTH_IN DOUBLE PRECISION,

NOTES BLOB sub_type 1 segment size 80,

GRAPHIC BLOB sub_type 0 segment size 80);

Для вывода изображений, сохраненных в поле GRAPHIC, мы будем использовать стандартный компонент DBIMagel: TDBImage. Очевидно также, что запросы при работе с BLOB-полями ничем не отличаются от запросов со стандартными типами полей:

SelectSQL:

SELECT * FROM BIOLIFE

UpdateSQL:

UPDATE BIOLIFE Set

ID=?NEW_ID,

CATEGORY=?NEW_CATEGORY,

COMMON_NAME=?NEW_COMMON_NAME,

SPECIES_NAME=?NEW_SPECIES_NAME,

LENGTH__CM_=?NEW_LENGTH_CM_,

LENGTH_IN=?NEW_LENGTH_IN,

NOTES=?NEW_NOTES,

GRAPHIC=?NEW_GRAPHIC

WHERE ID=?OLD_ID

Рис 2.66. Внешний вид формы приложения для работы с BLOB-полями

InsertSQL:

INSERT INTO BIOLIFE(

ID,

CATEGORY,

COMMON_NAME,

SPECIES_NAME,

LENGTH__CM_,

LENGTH_IN,

NOTES,

GRAPHIC

)

VALUES (

?NEW_ID,

?NEW_CATEGORY,

?NEW_COMMON_NAME,

?NEW_SPECIES_NAME,

?NEW_LENGTH_CM_,

?NEW_LENGTH_IN,

?NEW_NOTES ,

?NEW_GRAPHIC

)

DeleteSQL:

DELETE FROM BIOLIFE

WHERE ID=?OLD_ID

RefreshSQL:

SELECT * FROM BIOLIFE

WHERE

ID=?OLD_ID

Единственным отличием от обычных типов данных является то, что для присвоения значения BLOB-параметру необходимо использовать потоки (специализированные потомки стандартного класса TStream). Например, если мы хотим сохранить в нашем поле изображение из внешнего файла, то мы можем написать следующий обработчик нажатия на кнопку:

procedure TMainForm.OpenBClick(Sender: ТОbjесt);

var S. TStream;

FileS: TFileStream;

begin

if not OpenD.Execute then exit;

pFIBDataSetl.Edit;

S : =

pFIBDataSetl.CreateBlobStream(pFIBDataSetl.FieldByName('GRAPHIC') , bmReadWrite);

try

FileS := TFiIeStream.Create(OpenD.FileName, fmOpenRead);

S.CopyFrom(FileS, FileS.Size);

finally

FileS.Free;

S.Free;

pFIBDataSetl.Post;

end;

end;

Обратите внимание на важный момент - перед тем как присваивать значение BLOB-параметру, необходимо перевести pFIBDataSet в состояние редактирования данных. В данном случае это делается безусловным вызовом метода Edit. Вызов метода CreateBlobStream создает экземпляр специального внутреннего класса TFIBDSBlobStream. Скорее всего, вам не придется использовать этот класс напрямую. В нашем примере он нужен только для обмена данными между BLOB-параметром и потоком, который читает данные из файла с изображением. Параметр bmReadWrite означает, что мы собираемся изменять содержимое параметра. Поток S напрямую связан с BLOB-параметром, поэтому, копируя данные из файла (FileS) в поток S, мы фактически присваиваем значение параметру. Остается только сохранить изменения вызовом метода Post Аналогичным образом мы можем сохранить значение BLOB-поля в некоторый внешний файл:

procedure TMainForm.SaveBClick(Sender: TObject);

var S: TStream;

FileS: TFiIeStream;

begin

if not SaveD.Execute then exit;

if not pFIBDatasetl.FieldByName('GRAPHIC').IsNull then begin

S : =

pFIBDatasetl.CreaceBlobStream(pFIBDatasetl.FieldByName('GRAPHIC '), bmRead);

try

if FileExists(SaveD.FileName) then

FileS := TFileStream.Create(SaveD.FileName, fmOpenWrite)

else

FileS := TFileStream.Create(SaveD.FileName, fmCreate);

FileS.CopyFrom(S, S.Size);

finally

S.Free;

FileS.Free;

end;

end;

end;

Обратите внимание, что в этой процедуре мы используем параметр bmRead при создании потока S. Очевидно, что для сохранения содержимого BLOB-поля в файл нам не нужно изменять само поле, поэтому мы создаем поток только для чтения. Еще более простым способом мы можем очистить содержимое BLOB-поля:

procedure TMainForm.ButtonlClick(Sender: TObject);

begin

pFIBDataSetl .Edit;

pFIBDataSetl.FieldByName('GRAPHIC').Clear;

pFIBDataSetl.Post;

end;

В этом случае даже не требуется создавать какие-либо потоки. Иногда также нужно знать, является ли BLOB-поле пустым или нет. При использовании визуальных компонентов типа TDBImage мы не можем быть в этом уверены. Согласитесь, что никто не мешает нам "нарисовать" пустую картинку и сохранить ее в BLOB-поле В этом случае мы не сможем отличить "на глаз": есть ли какое-то изображение в BLOB-поле или нет. Однако мы можем написать обработчик события OnDataChange компонента DataSourcel: TDataSource:

procedure TMainForm.DataSourcelDataChange(Sender: TObject;

Field: TField) ;

begin

CheckBoxl.Checked :=

pFIBDataSetl.FieldByName('GRAPHIC').IsNull;

end;

Это событие вызывается, в частности, при навигации по DBGridl; таким образом, мы всегда можем узнать, является ли текущее поле пустым или нет.

Если вы используете TpFIBQuery для работы с BLOB-полями, то общий принцип остается тем же - необходимо использовать потоки, однако, в отличие от TpFIBDataSet, вам не потребуется создавать какие-то специальные потоки. Например, мы можем написать следующую процедуру, которая сохранит в файлы все изображения из нашей таблицы:

pFIBQuery.SQL: SELECT * FROM BIOLIFE

procedure TMainForm.Button2Click(Sender: TObject);

var SaveFile: TFileStream;

Index: Integer;

begin

with pFIBQueryl do begin ExecQuery;

Index := 1;

while not Eof do begin

SaveFile := TFileStream.Create(IntToStr(Index) + '.bmp',

fmCreate);

FN('GRAPHIC').SaveToStream(SaveFile);

Next;

inc(Index);

1 ... 41 42 43 44 45 46 47 48 49 ... 112
Перейти на страницу:
На этой странице вы можете бесплатно скачать Мир InterBase. Архитектура, администрирование и разработка приложений баз данных в InterBase/FireBird/Yaffil - А Ковязин торрент бесплатно.
Комментарии