На этом шаге мы рассмотрим еще один способ размещения в базе данных OLE-объекта.
Пример, приведенный на предыдущем шаге, имеет массу недостатков. Главным из них является тот факт, что пользователь должен вручную инициировать загрузку содержимого BLOB-поля в компонент TOleContainег и сохранение содержимого TOleContainer в таблице. Такое поведение приложения противоречит традиционному принципу создания пользовательских интерфейсов для форм просмотра и редактирования данных, гласящему, что данные в интерфейсных элементах при перемещении по записям, содержащимся в доставленном на рабочую станцию наборе данных, должны обновляться автоматически и сохраняться автоматически.
Если преодолеть первый недостаток данного приложения, добившись автоматического обновления данных в OLE-контейнере и сохранения их в таблице, тут же проявится его второй недостаток, заключающийся в постоянном сохранении и загрузке данных с диска. Дисковые операции вполне допустимы при ручных манипуляциях с OLE-коитейнером, но при автоматическом обновлении и сохранении они могут привести к заметному снижению производительности приложения.
Реализуя принцип автоматического обновления и сохранения данных при выполнении навигационных методов компонентов TDataSet, следует учитывать, что компонент TOleContainer не может быть связан с источником данных. Поэтому в простейшем случае необходимо использовать методы BeforeScroll и AfterScroll компонента-наследника класса TDataSet. В обработчике события BeforeScroll следует проверить, было ли изменено содержимое OLE-коитейпера. Это достигается путем проверки свойства Modified компонента TOleContainer. Если в этом компоненте были произведены какие-либо изменения, то создается поток данных в памяти, куда первоначально заносится некоторая последовательность байтов (назовем ее условно "цифровой подписью"; стоит, однако, отдавать себе отчет в том, что к настоящей цифровой подписи эта последовательность байтов отношения не имеет). Подпись будет в дальнейшем нужна при чтении данных: анализируя ее, можно определить, находятся ли данные для компонента TOleContainer в текущей записи. Затем используется метод SaveToStream компонента TOleContainer:
const Signature: Integer = -525465623; . . . . procedure TForm1.Table1BeforeScroll(DataSet: TDataSet); var Stream: TMemoryStream; begin if OleContainer1.Modified then begin Stream := nil; try Stream := TMemoryStream.Create; Stream.Write(Signature,SizeOf(Signature)); if Assigned(OleContainer1.OleObjectInterface) then OleContainer1.SaveToStream(Stream); Stream.Seek(0, soFromBeginning); try Table1.Edit; TBLOBField(Table1.FieldByName('Graphic')). LoadFromStream(Stream); Table1.Post; OleContainer1.Modified := False; except Table1.Cancel; end; finally if Assigned(Stream) then Stream.Free; end; end; end; procedure TForm1.Table1AfterScroll(DataSet: TDataSet); var Stream: TMemoryStream; N: Integer; begin OleContainer1.DestroyObject; Stream := nil; try Stream := TMemoryStream.Create; TBLOBField(Table1.FieldByName('Graphic')).SaveToStream(Stream); Stream.Seek(0, soFromBeginning); if Stream.Size > 4 then begin Stream.Read(N, SizeOf(N)); if N = Signature then OleContainer1.LoadFromStream(Stream); end; OleContainer1.Modified := False; finally if Assigned(Stream) then Stream.Free; end; end;
В заархивированной базе данных biolife.db содержимое поя Graphic у первых двух записей изменено. На рисунке 1 приведено содержимое этого поля у второй записи.
Рис.1. Результат работы приложения
Прежде всего обработчик события AfterScroll очищает текущее содержимое OLE-контейнера. После этого создается поток данных в памяти, куда и переносится содержимое BLOB-поля. Затем следует удостовериться, что поток данных содержит более 4 байт информации (иначе в нем не поместится даже "цифровая подпись"). После этого производится чтение "цифровой подписи" и при ее совпадении со значением константы Signature производится загрузка данных и установка свойства Modified равным False, что позволяет корректно определить, были ли произведены изменения содержимого OLE-контейнера после загрузки данных.
На следующем шаге мы рассмотрим создание OLE-контейнера в виде VCL-компонента.