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

Delphi 多層分布式開發實戰(二):客戶端 SQL 查詢、動態數據庫切換與主從表構建

     編輯:Delphi

系列回顧

上篇建立了基礎「應用程序服務器」(COM + DataSetProvider) 與「瘦客戶端」(TClientDataSet + TDCOMConnection)。本篇深入解決三大核心需求:

  1. 客戶端直接發送 SQL 查詢 (CommandText)。
  2. 運行時動態切換服務端數據庫別名/登錄。
  3. 多層架構下主從表關聯構建。

一、客戶端執行任意 SQL 查詢

關鍵設置:服務端 DataSetProvider.Options.poAllowCommandText := True。客戶端代碼:

ClientDataSet1.Close;
ClientDataSet1.CommandText := 'SELECT * FROM Orders WHERE Amount > 1000'; // 任意 SQL
ClientDataSet1.Open; // 自動傳給服務端 DataSetProvider 執行並返回結果集

原理:CommandText 通過 IProvider 接口傳遞給服務端 DataSetProvider,後者將其赋值給內部 TQuery.SQL 並 Open,結果數據包打包返回填充 ClientDataSet。

二、動態獲取並切換服務端數據庫別名

1. 服務端擴展:暴露方法獲取別名列表、設置連接

在類型庫編輯器中添加兩個方法:

  • GetDatabaseNames: OleVariant (返回字符串數組)
  • SetDatabaseName(const DBName, UserName, Password: WideString)

實現代碼:

function TRemoteDataModule.GetDatabaseNames: OleVariant;
var DBNames: TStringList; I: Integer;
begin
  DBNames := TStringList.Create;
  try
    Session1.GetDatabaseNames(DBNames); // BDE Session 獲取所有別名
    Result := VarArrayCreate([0, DBNames.Count-1], varOleStr); // 必須用 varOleStr 跨 COM
    for I := 0 to DBNames.Count-1 do Result[I] := DBNames[I];
  finally DBNames.Free; end;
end;

procedure TRemoteDataModule.SetDatabaseName(const DBName, UserName, Password: WideString);
begin
  try
    Database1.Close;
    Database1.AliasName := DBName;
    if (UserName <> '') and (Password <> '') then
    begin
      Database1.Params.Values['USER NAME'] := UserName;
      Database1.Params.Values['PASSWORD'] := Password;
    end;
    Database1.Open; // 嘗試連接,失敗拋出 EDBEngineError
  except
    on E: EDBEngineError do raise Exception.Create('Password Required'); // 統一異常文本供客戶端捕獲
  end;
end;

2. 客戶端界面與調用邏輯

  • ComboBox1:顯示別名列表 (FormCreate 時調用 AppServer.GetDatabaseNames 填充)。
  • 雙擊 ComboBox1 觸發切換:先嘗試空密碼調用 SetDatabaseName,捕獲 'Password Required' 異常彈出登錄對話框 (Form2),獲取用戶名/密碼後重試。
  • 切換成功後,刷新 ComboBox2 (表名列表) 和 ComboBox3 (字段名列表)。
procedure TForm1.ComboBox1DblClick(Sender: TObject);
var UserName, Password: string; DBTables: OleVariant; I: Integer;
begin
  if ComboBox1.Text = '' then Exit;
  ClientDataSet1.Close;
  try
    DCOMConnection1.AppServer.SetDatabaseName(ComboBox1.Text, '', ''); // 嘗試無密碼
  except
    on E: Exception do
      if E.Message = 'Password Required' then
      begin
        if InputDialog(UserName, Password) then // 彈出 Form2 獲取憑證
          DCOMConnection1.AppServer.SetDatabaseName(ComboBox1.Text, UserName, Password)
        else Exit;
      end else raise;
  end;
  // 刷新表名
  ComboBox2.Clear;
  DBTables := DCOMConnection1.AppServer.GetTableNames;
  if VarIsArray(DBTables) then
    for I := 0 to VarArrayHighBound(DBTables, 1) do ComboBox2.Items.Add(DBTables[I]);
  ComboBox2.Text := ComboBox2.Items[0];
  // 刷新字段名 (基於當前 ClientDataSet 打開的表)
  RefreshFieldList;
end;

3. 服務端新增 GetTableNames 方法

function TRemoteDataModule.GetTableNames: OleVariant;
var Tables: TStringList; I: Integer;
begin
  Tables := TStringList.Create;
  try
    Database1.GetTableNames(Tables, False); // BDE Database 獲取當前連接的表名
    Result := VarArrayCreate([0, Tables.Count-1], varOleStr);
    for I := 0 to Tables.Count-1 do Result[I] := Tables[I];
  finally Tables.Free; end;
end;

三、多層架構下主從表構建 (Master-Detail)

1. 服務端:單 DataSetProvider 輸出主表,關聯從表

  • ADOTable1 (主表) + ADOTable2 (從表)。
  • DataSource1.DataSet := ADOTable1;ADOTable2.MasterSource := DataSource1;ADOTable2.MasterFields := 'KeyField'。
  • 僅需 **一個** DataSetProvider1.DataSet := ADOTable1。從表不需單獨 Provider。

2. 客戶端:兩個 ClientDataSet 關聯

  • ClientDataSet1.RemoteServer := DCOMConnection1; ProviderName := 'DataSetProvider1'; Active := True。
  • 雙擊 ClientDataSet1 打開字段編輯器,右鍵 Add Fields,勾選所有主表字段,**關鍵:最後一行會出現「ADOTable2」(從表名)**,將其加入。這將從表作為主表的一個 TDataSetField 字段。
  • ClientDataSet2.DataSetField := ClientDataSet1ADOTable2 (即主表 ClientDataSet 中那個 DataSetField);ClientDataSet2.Active := True。

結果:ClientDataSet1 導航時,ClientDataSet2 自動過濾顯示對應從表記錄。雙綁定 DBGrid 即可呈現經典主從界面。

關鍵洞見

誤區正確認知
以為每個表都需要一個 DataSetProvider主從關係在服務端由 MasterSource/MasterFields 維護,客戶端只需一個 Provider 對應主表,從表通過 DataSetField 關聯獲取。
以為 CommandText 只能用於單表CommandText 可為任意合法 SQL (含 JOIN),服務端 Query 執行返回平坦結果集,客戶端視為單表。
以為動態切換數據庫需重啟服務SetDatabaseName 運行時修改 Database.AliasName/Params 並 Open 即時生效,無需重啟 COM。

總結

本篇實現了多層架構的三大進階能力:SQL 直通賦予客戶端極大查詢靈活性;動態數據庫切換解決多租戶/多數據源部署需求;主從表自動關聯證明了 DataSnap/MIDAS 完整支持層次化數據呈現。掌握 CommandText、AppServer 方法調用、DataSetField 關聯機制,是構建企業級多層 Delphi 應用的核心技能。

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