Welcome

首页 / 软件开发 / Delphi / 第二十章-开发Delphi对象式数据管理功能(二)(3)

第二十章-开发Delphi对象式数据管理功能(二)(3)2007-05-0720.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编程中很重要,即一定要修改相应数据库部件的状态。