首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 第 20 章:開發物件導向資料管理功能(二)─ TBlobStream 內部結構與建構/解構實作

     編輯:Delphi

20.1.7.2 TBlobStream 實作原理深度剖析

理解 TBlobStream 必須先認識其私有欄位定義:

TBlobStream = class(TStream)
private
  FField: TBlobField;      { 關聯的資料庫 BLOB 欄位 }
  FDataSet: TDataSet;      { 所屬資料集(TTable 或 TQuery) }
  FRecord: PChar;          { 內部緩衝區,儲存記錄資料(不含 BLOB) }
  FBuffer: PChar;          { 暫存記錄資料的緩衝區指標 }
  FFieldNo: Integer;       { BLOB 欄位編號 }
  FOpened: Boolean;        { 開啟狀態旗標 }
  FModified: Boolean;      { 修改旗標 }
  FPosition: Longint;      { 目前流位置 }
public
  ...
end;
  • FField:與 BLOB 流關聯的資料庫欄位,供內部存取。
  • FDataSet:包含該欄位的資料集元件。
  • FRecord / FBuffer:內部緩衝區,儲存記錄的非 BLOB 資料,作為 BDE API 參數。
  • FFieldNo:欄位編號,用於 BDE API 參數傳遞。
  • FOpened、FModified:狀態旗標。
  • FPosition:BLOB 流目前位置。

1. Create 建構子實作

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;

建構子流程:

  1. 以傳入 Field 初始化 FField、FDataSet、FRecord、FFieldNo。
  2. 若資料集處於 dsFilter 狀態則拋出例外。
  3. 若欄位未修改(FField.FModified = False):
    • 讀取模式(bmRead):配置記憶體緩衝區,複製目前記錄(不含 BLOB),設為唯讀模式。
    • 寫入模式(bmWrite/bmReadWrite):檢查資料集是否處於編輯/插入狀態,設為讀寫模式。
  4. 呼叫 DbiOpenBlob 開啟 BLOB 欄位。
  5. 設 FOpened := True;若為 bmWrite 則呼叫 Truncate 清空現有資料。

關鍵結論

  • 讀寫 BLOB 欄位時,資料集不得處於 Filter 狀態,否則觸發例外。
  • 必須將資料集設為 dsEdit 或 dsInsert 狀態才能操作 BLOB。
  • 若 BLOB 資料已修改,建立新 TBlobStream 時不會重複呼叫 DbiOpenBlob,僅設 FOpened := True,允許多個 BLOB 流同時操作同一欄位。

2. Destroy 解構子實作

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 流已修改,設定欄位 FModified := True。
  • 若欄位未修改,釋放 BLOB 資源(DbiFreeBlob)。
  • 釋放暫存緩衝區 FBuffer。
  • 若有修改,觸發欄位 DataChanged 事件通知資料變更。

重要觀察:BLOB 欄位修改直到 Destroy 時才真正提交,因為讀寫直接繞過 TField 呼叫 BDE API。使用 BDE API 程式設計時,務必同步更新對應資料庫元件的狀態。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved