20.1.7.2 TBlobStream的實現原理
說明TBlobStream對象的實現原理,不可避免地要涉及它的私有域,下面是私有域的定義:
TBlobStream = class(TStream)
private
FField: TBlobField;
FDataSet: TDataSet;
FRecord: PChar;
FBuffer: PChar;
FFieldNo: Integer;
FOpened: Boolean;
FModified: Boolean;
FPosition: Longint;
public
end;
FField是與BLOB流相聯的數據庫BLOB域,該域用於BLOB流的內部訪問。FDataSet是代表FField所在的數據庫,它可以是TTable部件,也可以是TQuery 部件。FRecord和FBuffer都是BLOB流內部使用的緩沖區,用於存儲FField所在記錄的數據,該數據記錄中不包含BLOB數據,TBlobStream使用FRecord作為調用BDE API函數的參數值。FFieldNo代表BLOB字段的字段號,也用於BDE API的參數傳遞,FOpened和FMocified都是狀態信息,FPosition表示BLOB流的當前位置,下面介紹TBlobStream方法實現。
1. Create方法和Destroy方法的實現
Create方法的功能主要是建立BlobStream流與BLOB字段的聯系並初始化某些私有變量。其實現如下:
constructor TBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
var
OpenMode: DbiOpenMode;
begin
FField := Field;
FDataSet := Field.DataSet;
FRecord := FDataSet.ActiveBuffer;
FFieldNo := Field.FieldNo;
if FDataSet.State = dsFilter then
DBErrorFmt(SNoFieldAccess, [FField.DisplayName]);
if not FField.FModified then
begin
if Mode = bmRead then
begin
FBuffer := AllocMem(FDataSet.RecordSize);
FRecord := FBuffer;
if not FDataSet.GetCurrentRecord(FBuffer) then Exit;
OpenMode := dbiReadOnly;
end else
begin
if not (FDataSet.State in [dsEdit, dsInsert]) then DBError(SNotEditing);
OpenMode := dbiReadWrite;
end;
Check(DbiOpenBlob(FDataSet.Handle, FRecord, FFieldNo, OpenMode));
end;
FOpened := True;
if Mode = bmWrite then Truncate;
end;
該方法首先是用傳入的Field參數給FField,FDataSet,FRecord和FFieldNo賦值。方法中用AllocMem按當前記錄大小分配內存,並將指針賦給FBuffer,用DataSet部件的GetCurrentRecord方法,將記錄的值賦給FBuffer,但不包括BLOB數據。
方法中用到的DbiOpenBlob函數是BDE的API函數,該函數用於打開數據庫中的BLOB字段。
最後如果方法傳入的Mode參數值為bmWrite,就調用Truncate將當前位置指針以後的
數據刪除。
分析這段源程序不難知道:
● 讀寫BLOB字段,不允許BLOB字段所在DataSet部件有Filter,否則產生異常事件
● 要讀寫BLOB字段,必須將DataSet設為編輯或插入狀態
● 如果BLOB字段中的數據作了修改,則在創建BLOB 流時,不再重新調用DBiOpenBlob函數,而只是簡單地將FOpened置為True,這樣可以用多個BLOB 流對同一個BLOB字段讀寫
Destroy方法釋放BLOB字段和為FBuffer分配的緩沖區,其實現如下:
destructor TBlobStream.Destroy;
begin
if FOpened then
begin
if FModified then FField.FModified := True;
if not FField.FModified then
DbiFreeBlob(FDataSet.Handle, FRecord, FFieldNo);
end;
if FBuffer <> nil then FreeMem(FBuffer, FDataSet.RecordSize);
if FModified then
try
FField.DataChanged;
except
Application.HandleException(Self);
end;
end;
如果BLOB流中的數據作了修改,就將FField的FModified置為True;如果FField的Modified為False就釋放BLOB字段,如果FBuffer不為空,則釋放臨時內存。最後根據FModified的值來決定是否啟動FField的事件處理過程DataChanged。
不難看出,如果BLOB字段作了修改就不釋放BLOB字段,並且對BLOB 字段的修改只有到Destroy時才提交,這是因為讀寫BLOB字段時都避開了FField,而直接調用BDE API函數。這一點是在應用BDE API編程中很重要,即一定要修改相應數據庫部件的狀態。