DELPHI開發2層C/S數據庫應用程序,許多人通過ADOQUERY或ADOTABLE直接操作數據庫,其實這種方法雖然最為直接,但有其缺點:如果以后要將程序升級為3層C/S會非常困難。而通過像下面的通用數據操作方法,像開發3層C/S一樣地開發2層C/S程序,通過ADOQUERY或ADOTABLE獲取數據,通過DATASETPROVIDER轉換數據格式為OLEVARIANT,通過CLIENTDATASET內存數據集再同客戶端顯示控件關聯,則所有操作數據的方法高度集中統一,以后要升級為多層會非常容易。這就是間接所帶來的高度靈活。
ADO不能像FIREDAC和UNIDAC一樣提交聯表查詢的CLIENTDATASET.DELTA,這在保存類似“單據”的數據的時候很不方便,需要間接一下然后才能提交。
FIREDAC和UNIDAC雖然默認也不支持,但只要設置屬性就能獲得支持。
其中FIREDAC的設置:UpdateOptions.CheckReadOnly:=False
其中的原由:A表聯接B表查詢,CLIENTDATASET中的字段既有A表的也有B表的,試圖將CLIENTDATASET.delta提交給A表,CLIENTDATASET.delta將B表的字段值改變也一塊打包了,而B表的字段的readonly:=true,試圖提交只讀字段會報錯。
unit untDB;
interface
uses
System.SysUtils, System.Classes, Data.DB, Data.Win.ADODB, Datasnap.Provider,
System.Variants, Vcl.Forms;
type
TfrmDB = class(TDataModule)
ADOConnection1: TADOConnection;
qryQuery: TADOQuery;
DataSetProvider1: TDataSetProvider;
qryExecute: TADOQuery;
procedure DataModuleCreate(Sender: TObject);
procedure qryQueryBeforeOpen(DataSet: TDataSet);
procedure qryQueryAfterOpen(DataSet: TDataSet);
private
{ Private declarations }
procedure connectDB;
public
{ Public declarations }
function querySQL(const sql: string): OleVariant;
function executeSQL(const sql: string): Boolean;
function saveData(const tableName: string; delta: OleVariant): Boolean;
function SaveDatas(tableNames, deltas: OleVariant;
tableCount: Integer): Boolean;
end;
var
frmDB: TfrmDB;
implementation
{ %CLASSGROUP 'Vcl.Controls.TControl' }
uses UnitWait;
{$R *.dfm}
{ TfrmDB }
procedure TfrmDB.connectDB;
begin
ADOConnection1.Close;
ADOConnection1.ConnectionString := 'FILE NAME=' +
ExtractFilePath(Application.ExeName) + 'db.udl';
try
ADOConnection1.Connected := True;
except
on E: Exception do
raise Exception.Create(E.Message);
end;
end;
procedure TfrmDB.DataModuleCreate(Sender: TObject);
begin
connectDB;
end;
function TfrmDB.executeSQL(const sql: string): Boolean;
begin
Result := False;
if sql = '' then
Exit;
qryExecute.Close;
qryExecute.sql.Clear;
qryExecute.sql.Text := sql;
Result := qryExecute.ExecSQL > 0;
end;
procedure TfrmDB.qryQueryAfterOpen(DataSet: TDataSet);
begin
FormWait.Close;
FormWait.Free;
end;
procedure TfrmDB.qryQueryBeforeOpen(DataSet: TDataSet);
begin
FormWait := TFormWait.Create(Application);
FormWait.Show;
FormWait.Update;
end;
function TfrmDB.querySQL(const sql: string): OleVariant;
begin
Result := null;
if sql = '' then
Exit;
qryQuery.Close;
qryQuery.sql.Clear;
qryQuery.sql.Text := sql;
qryQuery.Open;
Result := DataSetProvider1.Data;
end;
function TfrmDB.saveData(const tableName: string; delta: OleVariant): Boolean;
var
errCnt: Integer;
begin
Result := False;
if (tableName = '') or VarIsNull(delta) then
Exit;
qryQuery.Close;
qryQuery.sql.Clear;
qryQuery.sql.Text := 'select * from ' + tableName + ' where 1=2';
qryQuery.Open;
DataSetProvider1.ApplyUpdates(delta, 0, errCnt);
Result := errCnt = 0;
end;
function TfrmDB.SaveDatas(tableNames, deltas: OleVariant;
tableCount: Integer): Boolean;
var
i, errCnt: Integer;
begin
if not ADOConnection1.InTransaction then
ADOConnection1.BeginTrans; // 開啟事務
try
for i := 0 to tableCount - 1 do
begin
qryQuery.Close;
qryQuery.sql.Clear;
qryQuery.sql.Text := 'select * from ' + tableNames[i] + ' where 1=2';
qryQuery.Open;
DataSetProvider1.ApplyUpdates(deltas[i], 0, errCnt);
end;
ADOConnection1.CommitTrans; // 提交事務
Result := True;
except
ADOConnection1.RollbackTrans; // 回滾事務
Result := False;
end;
end;
end.