1. нет, объект класса BQueryClass все запросы выполняет в рамках одной транзакции:
Код: Выделить всё
Unit BQueryUnit;
{$mode objfpc}{$H+}
Interface
Uses
{$IFDEF TDS}
TDSCTDataBase,
{$ENDIF}
{$IFDEF UIB}
uib, uiblib,
{$ENDIF}
{$IFDEF IBX}
IBDatabase, IBSQL,
{$ENDIF}
Classes, SysUtils, BCommonUnit, BSQLUnit, LiteLCLProc;
Const
STEP_MAX = 3;
DB_FB = 1;
DB_MS = 2;
Type
{ BQueryClass }
BQueryClass = Class
Private
bID, bKind: Integer;
{$IFDEF UIB}
bUIBQuery: TUIBQuery;
bUIBDatabase: TUIBDataBase;
bUIBWrite, bUIBRead: TUIBTransaction;
{$ENDIF}
{$IFDEF TDS}
bTDSQuery: TTDSCTQuery;
bTDSDatabase: TTDSCTDataBase;
bTDSWrite, bTDSRead: TTDSCTTransaction;
{$ENDIF}
{$IFDEF IBX}
bIBXQuery: TIBSQL;
bIBXDatabase: TIBDataBase;
bIBXWrite, bIBXRead: TIBTransaction;
{$ENDIF}
{$IFDEF UIB}
Function GetUIB(Const aSQL: BSQLClass): Boolean;
Function PostUIB(Const aSQL: BSQLClass): Boolean;
{$ENDIF}
{$IFDEF TDS}
Function GetTDS(Const aSQL: BSQLClass): Boolean;
Function PostTDS(Const aSQL: BSQLClass): Boolean;
{$ENDIF}
{$IFDEF IBX}
Function GetIBX(Const aSQL: BSQLClass): Boolean;
Function PostIBX(Const aSQL: BSQLClass): Boolean;
{$ENDIF}
Public
Function ByString(Const aString: String): String;
Function ByDouble(Const aString: String): Double;
Function ByInteger(Const aString: String): Integer;
Function ByInt64(Const aString: String): Int64;
Function ByBoolean(Const aString: String): Boolean;
Function ByDate(Const aString: String): TDateTime;
Function ByDateTime(Const aString: String): TDateTime;
Function Get(Const aSQL: BSQLClass): Boolean;
Function Get(Const aString: String;
Const ForExecute: Boolean = FALSE): Boolean;
Function Post(Const aSQL: BSQLClass): Boolean;
Function Post(Const aString: String;
Const ForExecute: Boolean = FALSE): Boolean;
Function EOF: Boolean;
Procedure Go;
Procedure Next;
Procedure First;
Constructor Build(Const aID: Integer = 0);
Destructor Burn;
End;
Type
{ BDBManagerClass }
BDBManagerClass = Class
Strict Private
bPools: TThreadList;
Public
Procedure AddDatabase(Const aKind: Integer;
Const aServer, aBase, aUser, aPassword: String; Const aLib:String='');
Procedure RemoveDatabase(Const aIndex: Integer);
Function GetKind(Const aIndex: Integer): Integer;
Procedure Connect(Const aIndex: Integer = 0; Const aPoolSize: Integer = 1);
{$IFDEF UIB}
Procedure HoldUIBConnection(Const aIndex: Integer;
Out aDatabase: TUIBDataBase; Out aRead, aWrite: TUIBTransaction);
Procedure FreeUIBConnection(Const aIndex: Integer;
Const aDatabase: TUIBDataBase);
{$ENDIF}
{$IFDEF TDS}
Procedure HoldTDSConnection(Const aIndex: Integer;
Out aDatabase: TTDSCTDataBase; Out aRead, aWrite: TTDSCTTransaction);
Procedure FreeTDSConnection(Const aIndex: Integer;
Const aDatabase: TTDSCTDataBase);
{$ENDIF}
{$IFDEF IBX}
Procedure HoldIBXConnection(Const aIndex: Integer;
Out aDatabase: TIBDataBase; Out aRead, aWrite: TIBTransaction);
Procedure FreeIBXConnection(Const aIndex: Integer;
Const aDatabase: TIBDataBase);
{$ENDIF}
Procedure Disconnect(Const aIndex: Integer = 0);
Constructor Build;
Destructor Burn;
End;
Var
DBManager: BDBManagerClass;
Implementation
Const
STR_MSSQL = '/usr/lib/libct.so';
Type
{ BConnectionClass }
BConnectionClass = Class
Strict Private
bBusy: Boolean;
bKind: Integer;
{$IFDEF UIB}
bUIBDatabase: TUIBDataBase;
bReadUIBTransaction: TUIBTransaction;
bWriteUIBTransaction: TUIBTransaction;
{$ENDIF}
{$IFDEF TDS}
bTDSDatabase: TTDSCTDataBase;
bReadTDSTransaction: TTDSCTTransaction;
bWriteTDSTransaction: TTDSCTTransaction;
{$ENDIF}
{$IFDEF IBX}
bIBXDatabase: TIBDataBase;
bReadIBXTransaction: TIBTransaction;
bWriteIBXTransaction: TIBTransaction;
{$ENDIF}
Function GetConnected: Boolean;
Public
Property Kind: Integer Read bKind;
Property Busy: Boolean Read bBusy;
Property Connected: Boolean Read GetConnected;
{$IFDEF UIB}
Property UIBDatabase: TUIBDataBase Read bUIBDatabase;
Procedure HoldUIBConnection(Out aDatabase: TUIBDataBase;
Out aRead, aWrite: TUIBTransaction);
Procedure FreeUIBConnection;
{$ENDIF}
{$IFDEF TDS}
Property TDSDatabase: TTDSCTDataBase Read bTDSDatabase;
Procedure HoldTDSConnection(Out aDatabase: TTDSCTDataBase;
Out aRead, aWrite: TTDSCTTransaction);
Procedure FreeTDSConnection;
{$ENDIF}
{$IFDEF IBX}
Property IBXDatabase: TIBDataBase Read bIBXDatabase;
Procedure HoldIBXConnection(Out aDatabase: TIBDataBase;
Out aRead, aWrite: TIBTransaction);
Procedure FreeIBXConnection;
{$ENDIF}
Constructor Build(Const aKind: Integer;
Const aServer, aBase, aUser, aPassword, aLib: String);
Destructor Burn;
End;
Type
{ BConnectionsPoolClass }
BConnectionsPoolClass = Class
Strict Private
bConnected: Boolean;
bPool: TThreadList;
bBase: String;
bKind: Integer;
bLib: String;
bPassword: String;
bServer: String;
bUser: String;
bPoolDepth: Integer;
{$IFDEF UIB}
bUIBDatabase: TUIBDataBase;
bReadUIBTransaction: TUIBTransaction;
bWriteUIBTransaction: TUIBTransaction;
{$ENDIF}
{$IFDEF TDS}
bTDSDatabase: TTDSCTDataBase;
bReadTDSTransaction: TTDSCTTransaction;
bWriteTDSTransaction: TTDSCTTransaction;
{$ENDIF}
{$IFDEF IBX}
bIBXDatabase: TIBDataBase;
bReadIBXTransaction: TIBTransaction;
bWriteIBXTransaction: TIBTransaction;
{$ENDIF}
Public
Property Kind: Integer Read bKind;
Property Server: String Read bServer;
Property Base: String Read bBase;
Property User: String Read bUser;
Property Password: String Read bPassword;
Property Lib: String Read bLib;
Property Connected: Boolean Read bConnected;
Property PoolDepth: Integer Read bPoolDepth;
{$IFDEF UIB}
Property ReadUIBTransaction: TUIBTransaction Read bReadUIBTransaction;
Property WriteUIBTransaction: TUIBTransaction Read bWriteUIBTransaction;
Property UIBDatabase: TUIBDataBase Read bUIBDatabase;
Procedure HoldUIBConnection(Out aDatabase: TUIBDataBase;
Out aRead, aWrite: TUIBTransaction);
Procedure FreeUIBConnection(Const aDatabase: TUIBDataBase);
{$ENDIF}
{$IFDEF TDS}
Property ReadTDSTransaction: TTDSCTTransaction Read bReadTDSTransaction;
Property WriteTDSTransaction: TTDSCTTransaction Read bWriteTDSTransaction;
Property TDSDatabase: TTDSCTDataBase Read bTDSDatabase;
Procedure HoldTDSConnection(Out aDatabase: TTDSCTDataBase;
Out aRead, aWrite: TTDSCTTransaction);
Procedure FreeTDSConnection(Const aDatabase: TTDSCTDataBase);
{$ENDIF}
{$IFDEF IBX}
Property ReadIBXTransaction: TIBTransaction Read bReadIBXTransaction;
Property WriteIBXTransaction: TIBTransaction Read bWriteIBXTransaction;
Property IBXDatabase: TIBDataBase Read bIBXDatabase;
Procedure HoldIBXConnection(Out aDatabase: TIBDataBase;
Out aRead, aWrite: TIBTransaction);
Procedure FreeIBXConnection(Const aDatabase: TIBDataBase);
{$ENDIF}
Procedure Connect(Const aPoolDepth: Integer);
Procedure Disconnect;
Constructor Build(Const aKind: Integer;
Const aServer, aBase, aUser, aPassword, aLib: String);
Destructor Burn;
End;
{ BConnectionClass }
Function BConnectionClass.GetConnected: Boolean;
Begin
Result := FALSE;
Case bKind Of
DB_FB:
{$IFDEF UIB}
Result := Assigned(bUIBDatabase) And bUIBDatabase.Connected
{$ENDIF}
{$IFDEF IBX}
Result := Assigned(bIBXDatabase) And bIBXDatabase.Connected
{$ENDIF};
DB_MS:
{$IFDEF TDS}
Result := Assigned(bTDSDatabase) And bTDSDatabase.Connected
{$ENDIF};
End;
End;
{$IFDEF IBX}
Procedure BConnectionClass.HoldIBXConnection(Out aDatabase: TIBDataBase; Out
aRead, aWrite: TIBTransaction);
Begin
aDatabase := bIBXDatabase;
aRead := bReadIBXTransaction;
aWrite := bWriteIBXTransaction;
bBusy := TRUE;
End;
Procedure BConnectionClass.FreeIBXConnection;
Begin
If bReadIBXTransaction.InTransaction Then bReadIBXTransaction.Commit;
If bWriteIBXTransaction.InTransaction Then bWriteIBXTransaction.RollBack;
bBusy := FALSE;
End;
{$ENDIF}
{$IFDEF UIB}
Procedure BConnectionClass.HoldUIBConnection(Out aDatabase: TUIBDataBase;
Out aRead, aWrite: TUIBTransaction);
Begin
aDatabase := bUIBDatabase;
aRead := bReadUIBTransaction;
aWrite := bWriteUIBTransaction;
bBusy := TRUE;
End;
Procedure BConnectionClass.FreeUIBConnection;
Begin
If bReadUIBTransaction.InTransaction Then bReadUIBTransaction.Commit;
If bWriteUIBTransaction.InTransaction Then bWriteUIBTransaction.RollBack;
bBusy := FALSE;
End;
{$ENDIF}
{$IFDEF TDS}
Procedure BConnectionClass.HoldTDSConnection(Out aDatabase: TTDSCTDataBase;
Out aRead, aWrite: TTDSCTTransaction);
Begin
aDatabase := bTDSDatabase;
aRead := bReadTDSTransaction;
aWrite := bWriteTDSTransaction;
bBusy := TRUE;
End;
Procedure BConnectionClass.FreeTDSConnection;
Begin
If bReadTDSTransaction.Active Then bReadTDSTransaction.Commit;
If bWriteTDSTransaction.Active Then bWriteTDSTransaction.Rollback;
bBusy := FALSE;
End;
{$ENDIF}
Constructor BConnectionClass.Build(Const aKind: Integer; Const aServer, aBase,
aUser, aPassword, aLib: String);
Begin
bKind := aKind;
Case Kind Of
DB_FB:
Begin
{$IFDEF UIB}
bUIBDatabase := TUIBDataBase.Create(nil);
bUIBDatabase.DatabaseName := Format('%s:%s', [aServer, aBase]);
bUIBDatabase.UserName := aUser;
bUIBDatabase.PassWord := aPassword;
bUIBDatabase.CharacterSet := csUTF8;
If Not(aLib = '') Then bUIBDatabase.LibraryName := aLib;
bReadUIBTransaction := TUIBTransaction.Create(nil);
bReadUIBTransaction.DataBase := bUIBDatabase;
bReadUIBTransaction.Options :=
[tpRead, tpReadCommitted, tpNowait, tpRecVersion];
bWriteUIBTransaction := TUIBTransaction.Create(nil);
bWriteUIBTransaction.DataBase := bUIBDatabase;
bWriteUIBTransaction.Options := [tpWrite, tpNowait];
Try
bUIBDatabase.Connected := TRUE;
Except On E: Exception Do
SafeLog(E.Message);
End;
{$ENDIF};
{$IFDEF IBX}
bIBXDatabase := TIBDataBase.Create(nil);
bIBXDatabase.DatabaseName := Format('%s:%s', [aServer, aBase]);
bIBXDatabase.Params.Add(Format('user_name=%s', [aUser]));
bIBXDatabase.Params.Add(Format('password=%s', [aPassword]));
bIBXDatabase.Params.Add('lc_ctype=UTF-8');
bIBXDatabase.LoginPrompt := FALSE;
bReadIBXTransaction := TIBTransaction.Create(nil);
bReadIBXTransaction.DefaultDatabase := bIBXDatabase;
bReadIBXTransaction.DefaultAction := TACommit;
bReadIBXTransaction.Params.Add('read_committed');
bReadIBXTransaction.Params.Add('rec_version');
bReadIBXTransaction.Params.Add('nowait');
bWriteIBXTransaction := TIBTransaction.Create(nil);
bWriteIBXTransaction.DefaultDatabase := bIBXDatabase;
bWriteIBXTransaction.DefaultAction := TARollback;
bWriteIBXTransaction.Params.Add('write');
bWriteIBXTransaction.Params.Add('consistency');
Try
bIBXDatabase.Connected := TRUE;
Except On E: Exception Do
SafeLog(E.Message);
End;
{$ENDIF}
End;
DB_MS:
Begin
{$IFDEF TDS}
bTDSDatabase := TTDSCTDataBase.Create(nil);
bTDSDatabase.ServerVersion := svMSSQL2008;
bTDSDatabase.ServerName := aServer;
bTDSDatabase.Database := aBase;
bTDSDatabase.UserName := aUser;
bTDSDatabase.Password := aPassword;
bTDSDatabase.LibraryName := aLib;
bReadTDSTransaction := TTDSCTTransaction.Create(nil);
bReadTDSTransaction.DataBase := bTDSDatabase;
bWriteTDSTransaction := TTDSCTTransaction.Create(nil);
bWriteTDSTransaction.DataBase := bTDSDatabase;
Try
bTDSDatabase.Connected := TRUE;
Except On E: Exception Do
SafeLog(E.Message);
End;
{$ENDIF};
End;
End;
End;
Destructor BConnectionClass.Burn;
Begin
Case bKind Of
DB_FB:
Begin
{$IFDEF UIB}
If bReadUIBTransaction.InTransaction Then bReadUIBTransaction.Commit;
If bWriteUIBTransaction.InTransaction Then
bWriteUIBTransaction.Rollback;
bReadUIBTransaction.Free;
bWriteUIBTransaction.Free;
bUIBDatabase.Free;
{$ENDIF};
{$IFDEF IBX}
If bReadIBXTransaction.InTransaction Then bReadIBXTransaction.Commit;
If bWriteIBXTransaction.InTransaction THen
bWriteIBXTransaction.Rollback;
bReadIBXTransaction.Free;
bWriteIBXTransaction.Free;
bIBXDatabase.Free;
{$ENDIF}
End;
DB_MS:
Begin
{$IFDEF TDS}
If bReadTDSTransaction.Active Then bReadTDSTransaction.Commit;
If bWriteTDSTransaction.Active Then bWriteTDSTransaction.Rollback;
bReadTDSTransaction.Free;
bWriteTDSTransaction.Free;
bTDSDatabase.Free;
{$ENDIF};
End;
End;
End;
{ BConnectionPoolClass }
{$IFDEF UIB}
Procedure BConnectionsPoolClass.HoldUIBConnection(Out aDatabase: TUIBDataBase;
Out aRead, aWrite: TUIBTransaction);
Var
i, aStep: Integer;
aWasFound: Boolean;
aConnection: BConnectionClass;
Begin
aStep := 0;
aWasFound := FALSE;
With bPool.LockList Do
Repeat
For i := 0 To Count - 1 Do
Begin
aConnection := BConnectionClass(Items[i]);
If Not(aConnection.Busy) Then
Begin
aConnection.HoldUIBConnection(aDatabase, aRead, aWrite);
aWasFound := TRUE;
Break;
End;
Inc(aStep);
Sleep(20);
End;
Until aWasFound Or (aStep < STEP_MAX);
bPool.UnlockList;
End;
Procedure BConnectionsPoolClass.FreeUIBConnection(
Const aDatabase: TUIBDataBase);
Var
i: Integer;
Begin
With bPool.LockList Do
For i := 0 To Count - 1 Do
If BConnectionClass(Items[i]).UIBDatabase = aDatabase Then
Begin
BConnectionClass(Items[i]).FreeUIBConnection;
Break;
End;
bPool.UnlockList;
End;
{$ENDIF}
{$IFDEF TDS}
Procedure BConnectionsPoolClass.HoldTDSConnection(Out aDatabase: TTDSCTDataBase;
Out aRead, aWrite: TTDSCTTransaction);
Var
i, aStep: Integer;
aWasFound: Boolean;
aConnection: BConnectionClass;
Begin
aWasFound := FALSE;
aStep := 0;
With bPool.LockList Do
Repeat
For i := 0 To Count - 1 Do
Begin
aConnection := BConnectionClass(Items[i]);
If Not(aConnection.Busy) Then
Begin
aConnection.HoldTDSConnection(aDatabase, aRead, aWrite);
aWasFound := TRUE;
Break;
End;
Inc(aStep);
Sleep(20);
End;
Until aWasFound Or (aStep < STEP_MAX);
bPool.UnlockList;
End;
Procedure BConnectionsPoolClass.FreeTDSConnection(
Const aDatabase: TTDSCTDataBase);
Var
i: Integer;
Begin
With bPool.LockList Do
For i := 0 To Count - 1 Do
If BConnectionClass(Items[i]).TDSDatabase = aDatabase Then
Begin
BConnectionClass(Items[i]).FreeTDSConnection;
Break;
End;
bPool.UnlockList;
End;
{$ENDIF}
{$IFDEF IBX}
Procedure BConnectionsPoolClass.HoldIBXConnection(Out aDatabase: TIBDataBase;
Out aRead, aWrite: TIBTransaction);
Var
i, aStep: Integer;
aWasFound: Boolean;
aConnection: BConnectionClass;
Begin
aWasFound := FALSE;
aStep := 0;
With bPool.LockList Do
Repeat
For i := 0 To Count - 1 Do
Begin
aConnection := BConnectionClass(Items[i]);
If Not(aConnection.Busy) Then
Begin
aConnection.HoldIBXConnection(aDatabase, aRead, aWrite);
aWasFound := TRUE;
Break;
End;
Inc(aStep);
Sleep(20);
End;
Until aWasFound Or (aStep < STEP_MAX);
bPool.UnlockList;
End;
Procedure BConnectionsPoolClass.FreeIBXConnection(
Const aDatabase: TIBDataBase);
Var
i: Integer;
Begin
With bPool.LockList Do
For i := 0 To Count - 1 Do
If BConnectionClass(Items[i]).IBXDatabase = aDatabase Then
Begin
BConnectionClass(Items[i]).FreeIBXConnection;
Break;
End;
bPool.UnlockList;
End;
{$ENDIF}
Procedure BConnectionsPoolClass.Connect(Const aPoolDepth: Integer);
Var
i: Integer;
aConnection: BConnectionClass;
Begin
If Connected Then
Begin
Raise Exception.Create('Already Connected');
Exit;
End;
If Not(PoolDepth = 0) Then SafeLog('Pool not empty on connect');
bPoolDepth := 0;
For i := 1 To aPoolDepth Do
Begin
aConnection := BConnectionClass.Build(Kind,Server,Base,User,Password,Lib);
If aConnection.Connected Then
Begin
bPool.Add(aConnection);
Inc(bPoolDepth);
End;
End;
If Not(aPoolDepth=PoolDepth) Then SafeLog('Not all connections established');
bConnected := TRUE;
End;
Procedure BConnectionsPoolClass.Disconnect;
Var
i: Integer;
Begin
With bPool.LockList Do
Begin
For i := 0 To Count - 1 Do
BConnectionClass(Items[i]).Burn;
bPoolDepth := 0;
bConnected := FALSE;
Clear;
End;
bPool.UnlockList
End;
Constructor BConnectionsPoolClass.Build(Const aKind: Integer; Const aServer, aBase,
aUser, aPassword, aLib: String);
Begin
bKind := aKind;
bServer := aServer;
bBase := aBase;
bUser := aUser;
bPassword := aPassword;
bLib := aLib;
bPool := TThreadList.Create;
End;
Destructor BConnectionsPoolClass.Burn;
Var
i: Integer;
Begin
With bPool.LockList Do
For i := 0 To Count - 1 Do
BConnectionClass(Items[i]).Burn;
bPool.UnlockList;
bPool.Free;
End;
{ BQueryClass }
Constructor BQueryClass.Build(Const aID: Integer);
Begin
{$IFDEF UIB}
bUIBQuery := TUIBQuery.Create(nil);
{$ENDIF}
{$IFDEF TDS}
bTDSQuery := TTDSCTQuery.Create(nil);
{$ENDIF}
{$IFDEF IBX}
bIBXQuery := TIBSQL.Create(nil);
{$ENDIF}
bID := aID;
bKind := DBManager.GetKind(aID);
Case bKind Of
DB_FB:
Begin
{$IFDEF UIB}
DBManager.HoldUIBConnection(aID, bUIBDatabase, bUIBRead,
bUIBWrite);
bUIBQuery.DataBase := bUIBDatabase;
{$ENDIF}
{$IFDEF IBX}
DBManager.HoldIBXConnection(aID, bIBXDatabase, bIBXRead, bIBXWrite);
bIBXQuery.Database := bIBXDatabase;
{$ENDIF}
End;
DB_MS:
Begin
{$IFDEF TDS}
DBManager.HoldTDSConnection(aID, bTDSDatabase, bTDSRead,
bTDSWrite);
bTDSQuery.DataBase := bTDSDatabase;
{$ENDIF}
End;
End;
End;
Destructor BQueryClass.Burn;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}
DBManager.FreeUIBConnection(bID, bUIBQuery.DataBase)
{$ENDIF}
{$IFDEF IBX}
DBManager.FreeIBXConnection(bID, bIBXQuery.Database)
{$ENDIF};
DB_MS:
{$IFDEF TDS}
DBManager.FreeTDSConnection(bID, bTDSQuery.DataBase)
{$ENDIF};
End;
End;
{$IFDEF UIB}
Function BQueryClass.GetUIB(Const aSQL: BSQLClass): Boolean;
Var
i: Integer;
Begin
Result := FALSE;
With bUIBQuery Do
Begin
Transaction := bUIBRead;
If Not(Transaction.InTransaction) Then Transaction.StartTransaction;
Params.Clear;
SQL.Clear;
SQL.Add(aSQL.SQL);
For i := 0 To aSQL.Params.Count - 1 Do
Params.ByNameAsString[BSQLParamClass(aSQL.Params[i]).Iterator] :=
BSQLParamClass(aSQL.Params[i]).Value;
Try
If aSQL.IsProcedure Then Execute
Else Open;
Except On E: Exception Do
Begin
Log(SQL.Text);
Log(E.Message);
End;
End;
End;
Result := TRUE;
End;
Function BQueryClass.PostUIB(Const aSQL: BSQLClass): Boolean;
Var
i: Integer;
aParam: BSQLParamClass;
Begin
Result := FALSE;
With bUIBQuery Do
Begin
Transaction := bUIBWrite;
If Not(Transaction.InTransaction) Then Transaction.StartTransaction;
Try
Params.Clear;
SQL.Text := aSQL.SQL;
For i := 0 To aSQL.Params.Count - 1 Do
Begin
aParam := BSQLParamClass(aSQL.Params[i]);
If aParam.Stream = nil Then
Params.ByNameAsString[aParam.Iterator] := aParam.Value
Else
ParamsSetBlob(aParam.Iterator, aParam.Stream);
End;
If aSQL.IsProcedure Then Execute
Else ExecSQL;
Except On E: Exception Do
Begin
Log(SQL.Text);
Log(E.Message);
Transaction.Rollback;
Exit;
End;
End;
End;
Result := TRUE;
End;
{$ENDIF}
{$IFDEF TDS}
Function BQueryClass.GetTDS(Const aSQL: BSQLClass): Boolean;
Var
i: Integer;
Begin
Result := FALSE;
With bTDSQuery Do
Begin
Transaction := bTDSRead;
Params.Clear;
SQL.Text := aSQL.SQL;
For i := 0 To aSQL.Params.Count - 1 Do
Params.ParamByName(BSQLParamClass(aSQL.Params[i]).Iterator).AsString :=
BSQLParamClass(aSQL.Params[i]).Value;
Try
If aSQL.IsProcedure Then ExecSQL
Else Open;
Except On E: Exception Do
Begin
DirectLog(SQL.Text);
Log(E.Message);
End;
End;
End;
Result := TRUE;
End;
Function BQueryClass.PostTDS(Const aSQL: BSQLClass): Boolean;
Var
i: Integer;
aParam: BSQLParamClass;
Begin
Result := FALSE;
With bTDSQuery Do
Begin
Transaction := bTDSWrite;
If Not(Transaction.Active) Then Transaction.StartTransaction;
Try
Params.Clear;
SQL.Text := aSQL.SQL;
For i := 0 To aSQL.Params.Count - 1 Do
Begin
aParam := BSQLParamClass(aSQL.Params[i]);
// TODO: no blob support
If aParam.Stream = nil Then
ParamByName(aParam.Iterator).Value := aParam.Value;
End;
ExecSQL;
Except On E: Exception Do
Begin
Log(SQL.Text);
Log(E.Message);
Transaction.Rollback;
Exit;
End;
End;
End;
Result := TRUE;
End;
{$ENDIF}
{$IFDEF IBX}
Function BQueryClass.GetIBX(Const aSQL: BSQLClass): Boolean;
Var
i: Integer;
Begin
Result := FALSE;
With bIBXQuery Do
Begin
If Open Then Close;
Database := bIBXDatabase;
Transaction := bIBXRead;
If Not(Transaction.InTransaction) Then Transaction.StartTransaction;
SQL.Text := aSQL.SQL;
For i := 0 To aSQL.Params.Count - 1 Do
Params.ByName(BSQLParamClass(aSQL.Params[i]).Iterator).AsString :=
BSQLParamClass(aSQL.Params[i]).Value;
Try
ExecQuery;
Except On E: Exception Do
Begin
SafeLog(SQL.Text);
SafeLog(E.Message);
End;
End;
End;
Result := TRUE;
End;
Function BQueryClass.PostIBX(Const aSQL: BSQLClass): Boolean;
Var
i: Integer;
aParam: BSQLParamClass;
Begin
Result := FALSE;
With bIBXQuery Do
Begin
Transaction := bIBXWrite;
If Not(Transaction.InTransaction) Then Transaction.StartTransaction;
Try
SQL.Text := aSQL.SQL;
For i := 0 To aSQL.Params.Count - 1 Do
Begin
aParam := BSQLParamClass(aSQL.Params[i]);
// TODO: no blob support
If aParam.Stream = nil Then
ParamByName(aParam.Iterator).Value := aParam.Value;
End;
ExecQuery;
Except On E: Exception Do
Begin
SafeLog(SQL.Text);
SafeLog(E.Message);
Transaction.Rollback;
Exit;
End;
End;
End;
Result := TRUE;
End;
{$ENDIF}
Function BQueryClass.ByString(Const aString: String): String;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsString[aString]{$ENDIF}
{$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsString{$ENDIF};
DB_MS:
{$IFDEF TDS}Result := bTDSQuery.ByNameAsString[aString]{$ENDIF};
End;
End;
Function BQueryClass.ByDouble(Const aString: String): Double;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsDouble[aString]{$ENDIF}
{$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsFloat{$ENDIF};
DB_MS:
{$IFDEF TDS}Result := bTDSQuery.ByNameAsFloat[aString]{$ENDIF};
End;
End;
Function BQueryClass.ByInteger(Const aString: String): Integer;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsInteger[aString]{$ENDIF}
{$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsInteger{$ENDIF};
DB_MS:
{$IFDEF TDS}Result := bTDSQuery.ByNameAsInteger[aString]{$ENDIF};
End;
End;
Function BQueryClass.ByInt64(Const aString: String): Int64;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsInt64[aString]{$ENDIF}
{$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsInt64{$ENDIF};
DB_MS:
{$IFDEF TDS}Result := bTDSQuery.ByNameAsInt64[aString]{$ENDIF};
End;
End;
Function BQueryClass.ByBoolean(Const aString: String): Boolean;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}Result:=bUIBQuery.Fields.ByNameAsBoolean[aString]{$ENDIF}
{$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsInteger=1{$ENDIF};
DB_MS:
{$IFDEF TDS}Result := bTDSQuery.ByNameAsInteger[aString] = 1{$ENDIF};
End;
End;
Function BQueryClass.ByDate(Const aString: String): TDateTime;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsDate[aString]{$ENDIF}
{$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsDateTime{$ENDIF};
DB_MS:
{$IFDEF TDS}Result := bTDSQuery.ByNameAsDate[aString]{$ENDIF};
End;
End;
Function BQueryClass.ByDateTime(Const aString: String): TDateTime;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsDateTime[aString]{$ENDIF}
{$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsDateTime{$ENDIF};
DB_MS:
{$IFDEF TDS}Result := bTDSQuery.ByNameAsDateTime[aString]{$ENDIF};
End;
End;
Function BQueryClass.Get(Const aSQL: BSQLClass): Boolean;
Begin
Result := FALSE;
Case bKind Of
DB_FB:
{$IFDEF UIB}Result := GetUIB(aSQL){$ENDIF}
{$IFDEF IBX}Result := GetIBX(aSQL){$ENDIF};
DB_MS:
{$IFDEF TDS}Result := GetTDS(aSQL){$ENDIF};
End;
End;
Function BQueryClass.Get(Const aString: String;
Const ForExecute: Boolean = FALSE): Boolean;
Var
aSQL: BSQLClass;
Begin
aSQL := BSQLClass.Build(aString, ForExecute);
Result := Get(aSQL);
aSQL.Burn;
End;
Function BQueryClass.Post(Const aSQL: BSQLClass): Boolean;
Begin
Result := FALSE;
Case bKind Of
DB_FB:
{$IFDEF UIB}Result := PostUIB(aSQL){$ENDIF}
{$IFDEF IBX}Result := PostIBX(aSQL){$ENDIF};
DB_MS:
{$IFDEF TDS}Result := PostTDS(aSQL){$ENDIF};
End;
End;
Function BQueryClass.Post(Const aString: String;
Const ForExecute: Boolean): Boolean;
Var
aSQL: BSQLClass;
Begin
aSQL := BSQLClass.Build(aString, ForExecute);
Result := Post(aSQL);
aSQL.Burn;
End;
Function BQueryClass.EOF: Boolean;
Begin
Result := TRUE;
Case bKind Of
DB_FB:
{$IFDEF UIB}Result := bUIBQuery.Eof{$ENDIF}
{$IFDEF IBX}Result := bIBXQuery.EOF{$ENDIF};
DB_MS:
{$IFDEF TDS}Result := bTDSQuery.Eof{$ENDIF};
End;
End;
Procedure BQueryClass.Go;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}
With bUIBQuery Do
Begin
Transaction := bUIBWrite;
If Transaction.InTransaction Then Transaction.Commit;
End{$ENDIF}
{$IFDEF IBX}
With bIBXQuery Do
Begin
Transaction := bIBXWrite;
If Transaction.InTransaction Then Transaction.Commit;
End{$ENDIF};
DB_MS:
{$IFDEF TDS}
With bTDSQuery Do
Begin
Transaction := bTDSWrite;
If Transaction.Active Then Transaction.Commit;
End{$ENDIF};
End;
End;
Procedure BQueryClass.Next;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}bUIBQuery.Next{$ENDIF}
{$IFDEF IBX}bIBXQuery.Next{$ENDIF};
DB_MS:
{$IFDEF TDS}bTDSQuery.Next{$ENDIF};
End;
End;
Procedure BQueryClass.First;
Begin
Case bKind Of
DB_FB:
{$IFDEF UIB}bUIBQuery.First{$ENDIF}
{$IFDEF IBX}Raise Exception.Create('Not implemented'){$ENDIF};
DB_MS:
{$IFDEF TDS}Raise Exception.Create('Not implemented'){$ENDIF};
End;
End;
{ BDBManagerClass }
Constructor BDBManagerClass.Build;
Begin
bPools := TThreadList.Create;
End;
Destructor BDBManagerClass.Burn;
Var
i: Integer;
Begin
With bPools.LockList Do
For i := 0 To Count - 1 Do
BConnectionsPoolClass(Items[i]).Burn;
bPools.UnlockList;
bPools.Free;
End;
Procedure BDBManagerClass.AddDatabase(Const aKind: Integer;
Const aServer, aBase, aUser, aPassword: String; Const aLib: String);
Begin
bPools.Add(
BConnectionsPoolClass.Build(aKind, aServer, aBase, aUser, aPassword, aLib));
End;
Procedure BDBManagerClass.RemoveDatabase(Const aIndex: Integer);
Var
aConnectionPool: BConnectionsPoolClass;
Begin
With bPools.LockList Do
Begin
aConnectionPool := BConnectionsPoolClass(Items[aIndex]);
aConnectionPool.Burn;
Delete(aIndex);
End;
bPools.UnlockList;
End;
Function BDBManagerClass.GetKind(Const aIndex: Integer): Integer;
Var
aConnectionPool: BConnectionsPoolClass;
Begin
aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
bPools.UnlockList;
Result := aConnectionPool.Kind;
End;
Procedure BDBManagerClass.Connect(Const aIndex: Integer;
Const aPoolSize: Integer);
Var
aConnectionPool: BConnectionsPoolClass;
Begin
aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
bPools.UnlockList;
aConnectionPool.Connect(aPoolSize);
End;
{$IFDEF IBX}
Procedure BDBManagerClass.HoldIBXConnection(Const aIndex: Integer;
Out aDatabase: TIBDataBase; Out aRead, aWrite: TIBTransaction);
Var
aConnectionPool: BConnectionsPoolClass;
Begin
aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
bPools.UnlockList;
aConnectionPool.HoldIBXConnection(aDatabase, aRead, aWrite);
End;
Procedure BDBManagerClass.FreeIBXConnection(Const aIndex: Integer;
Const aDatabase: TIBDataBase);
Var
aConnectionPool: BConnectionsPoolClass;
Begin
aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
bPools.UnlockList;
aConnectionPool.FreeIBXConnection(aDatabase);
End;
{$ENDIF}
{$IFDEF UIB}
Procedure BDBManagerClass.HoldUIBConnection(Const aIndex: Integer;
Out aDatabase: TUIBDataBase; Out aRead, aWrite: TUIBTransaction);
Var
aConnectionPool: BConnectionsPoolClass;
Begin
aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
bPools.UnlockList;
aConnectionPool.HoldUIBConnection(aDatabase, aRead, aWrite);
End;
Procedure BDBManagerClass.FreeUIBConnection(Const aIndex: Integer;
Const aDatabase: TUIBDataBase);
Var
aConnectionPool: BConnectionsPoolClass;
Begin
aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
bPools.UnlockList;
aConnectionPool.FreeUIBConnection(aDatabase);
End;
{$ENDIF}
{$IFDEF TDS}
Procedure BDBManagerClass.HoldTDSConnection(Const aIndex: Integer;
Out aDatabase: TTDSCTDataBase; Out aRead, aWrite: TTDSCTTransaction);
Var
aConnectionPool: BConnectionsPoolClass;
Begin
aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
bPools.UnlockList;
aConnectionPool.HoldTDSConnection(aDatabase, aRead, aWrite);
End;
Procedure BDBManagerClass.FreeTDSConnection(Const aIndex: Integer;
Const aDatabase: TTDSCTDataBase);
Var
aConnectionPool: BConnectionsPoolClass;
Begin
aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
bPools.UnlockList;
aConnectionPool.FreeTDSConnection(aDatabase);
End;
{$ENDIF}
Procedure BDBManagerClass.Disconnect(Const aIndex: Integer);
Var
aConnectionPool: BConnectionsPoolClass;
Begin
aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
bPools.UnlockList;
aConnectionPool.Disconnect;
End;
Initialization
Begin
DBManager := BDBManagerClass.Build;
End;
Finalization
Begin
DBManager.Burn;
End;
End.
это моя обёртка над UIB, FreeTDS и, теперь, IBX, позволяющая в некотором смысле забыть о транзакциях/соединениях/библиотеках доступа (в мечтах приделать в неё бекэнд для PostgreSQL и SQLite). Пока далёк от идеала, но если пользоваться зная опасные места - то работает ^_^ Уже довольно успешно во всех моих проектах с БД. На каких-нибудь праздниках соберусь и избавлюсь от глобальных переменных, откорректирую реализацию "ConnectionPool'a" и т.д. Но и сейчас оно работает и уже дважды мне позволила без переписывания проектов поменять библиотеку общения с базой ^_^
2. Под "реляциями" понимаются ссылки между объектами? Если да, то не забыл и именно их имел в виду под "...как быть, если Object является полем совсем другого объекта...". Пока решил волевым решением - одновременно должна меняться только одна сущность (объекты одного управляемого класса). Все остальные части программы должны видеть только то, что было "закоммичено". Если же вдруг потребуется доступ к информации об изменениях, сделанных до вызова Commit, то нужно пересмотреть логику программы - нельзя ли каким-то образом избежать этого требования, затем
- либо всеми правдами и неправдами добираться до Manager'а, производящего изменения и все обращения к "реляциям" (если я не ошибся выше ^_^) прогонять через CheckObject;
- либо добавить ещё глобальную надстройку, в которой буду регистрировать все изменения, вместе с изменяющими менеджерами и прогонять все те же "реляции" через эту надстройку, по возможности наименее заметно;
- либо рвать себе все волосы на всех местах и отказываться от чуда объектов и мечт о лёгкой и ненапряжной ORM системе ^_^
Как-то так... Сейчас вроде как закончил тестирование на одной паре унаследованных от этих базовых классов объектах - пока они работают ^_^ Перехожу к классам с ссылкой на уже обновленное ^_^ Сейчас базовый класс выглядит так -
Код: Выделить всё
Unit BObjectUnit;
{$mode objfpc}{$H+}
Interface
Uses
Classes, SysUtils, BQueryUnit, BCommonUnit;
Const
DEFAULT_ID = -1;
Type BORMStateKind = (ormUnknow, ormCommited, ormChanged, ormAdded, ormDeleted);
Type
{ BObjectClass }
BObjectClass = Class
Private
bORMState: BORMStateKind;
Protected
bID: Integer;
Procedure Load(Const aObject: BObjectClass); Virtual; Abstract;
Function BuildClone: BObjectClass; Virtual; Abstract;
Function GetJSON: String; Virtual; Abstract;
Public
Property ID: Integer Read bID;
Property ORMState: BORMStateKind Read bORMState;
Property JSON: String Read GetJSON;
Function Save(aQuery: BQueryClass = nil): Boolean; Virtual; Abstract;
Function Delete(aQuery: BQueryClass = nil): Boolean; Virtual; Abstract;
Constructor Build; Virtual;
Destructor Burn; Virtual; Abstract;
End;
Type
{ BTreeObjectClass }
BTreeObjectClass = Class(BObjectClass)
Private
Procedure SetParent(Const aValue: BTreeObjectClass);
Protected
bParent: BTreeObjectClass;
bChildren: TThreadList;
Function GetNodeJSON: String; Virtual; Abstract;
Public
Property Parent: BTreeObjectClass Read bParent Write SetParent;
Property NodeJSON: String Read GetNodeJSON;
End;
Type
{ BManagerClass }
BManagerClass = Class
Private
bModified: Boolean;
Function FindByID(Const aList: TList; Const aID: Integer): BObjectClass;
Function FindByID(Const aList: TThreadList;
Const aID: Integer): BObjectClass;
Procedure PurgeList(Const aList: TThreadList);
Protected
bDeleted: TThreadList;
bManaged: TThreadList;
bNonCommited: TThreadList;
bDBIndex, bNonCommitedIndex: Integer;
Procedure AfterLoad;
Procedure CheckObject(Var aObject: BObjectClass);
Function RequestQueue(Const aObject: BObjectClass): Boolean; Virtual;
Function RequestChange(Const aObject: BObjectClass): BObjectClass;
Procedure RequestDelete(Const aObject: BObjectClass);
Function RequestObject(Const aID: Integer): BObjectClass;
Procedure ProcessCommit(Const aObject: BObjectClass;
Const aORMState: BORMStateKind); Virtual; Abstract;
Public
Function GetJSON: TStringList; Virtual;
Function Extract(aCommaSeparatedObjects: String): TList;
Function GetNodeJSON(Const aParentObject:BTreeObjectClass): String; Virtual;
Procedure Commit; Virtual;
Constructor Build(Const aManagedList: TThreadList;
Const aDBIndex: Integer); Virtual;
Destructor Burn; Virtual;
End;
Implementation
ResourceString
STR_ORM_ERROR_GET = 'ORM object request error occurred. ID can''t be "-1"';
STR_ORM_ERROR_BUILD = 'ORM queue build error occurred. ID should be "-1"';
STR_ORM_ERROR_CHANGE = 'ORM change request error occurred. ID can''t be "-1"';
STR_ORM_ERROR_DELETE = 'ORM delete request error occurred. ID can''t be "-1"';
{ BObjectClass }
Constructor BObjectClass.Build;
Begin
bORMState := ormCommited;
End;
{ BTreeObjectClass }
Procedure BTreeObjectClass.SetParent(Const aValue: BTreeObjectClass);
Begin
If bORMState = ormCommited Then
If Not(bParent = nil) Then bParent.bChildren.Remove(Self);
bParent:=aValue;
If bORMState = ormCommited Then
If Not(bParent = nil) Then bParent.bChildren.Add(Self);
End;
{ BManagerClass }
Function BManagerClass.FindByID(Const aList: TList;
Const aID: Integer): BObjectClass;
Var
i: Integer;
Begin
Result := nil;
For i := 0 To aList.Count - 1 Do
If BObjectClass(aList[i]).ID = aID Then
Begin
Result := BObjectClass(aList[i]);
Exit;
End;
End;
Function BManagerClass.FindByID(Const aList: TThreadList;
Const aID: Integer): BObjectClass;
Var
i: Integer;
Begin
Result := nil;
If aID = -1 Then Exit;
Result := FindByID(aList.LockList, aID);
aList.UnlockList;
End;
Procedure BManagerClass.CheckObject(Var aObject: BObjectClass);
Var
aBufferObject: BObjectClass;
Begin
// check for nil/ID
If (aObject = nil) Or (aObject.ID = -1) Then
Raise Exception(STR_ORM_ERROR_BUILD);
If Not(bModified) Then Exit;
aBufferObject := FindByID(bDeleted, aObject.ID);
If Not(aBufferObject = nil) Then aObject := nil;
If aObject = nil Then Exit;
aBufferObject := FindByID(bNonCommited, aObject.ID);
If Not(aBufferObject = nil) Then aObject := aBufferObject
End;
Procedure BManagerClass.PurgeList(Const aList: TThreadList);
Var
i: Integer;
Begin
With aList.LockList Do
For i := 0 To Count - 1 Do
BObjectClass(Items[i]).Burn;
aList.UnlockList;
aList.Free;
End;
Procedure BManagerClass.AfterLoad;
Var
i: Integer;
Begin
With bManaged.LockList Do
For i := 0 To Count - 1 Do
BObjectClass(Items[i]).bORMState := ormCommited;
bManaged.UnlockList;
End;
Function BManagerClass.RequestQueue(Const aObject: BObjectClass): Boolean;
Begin
//Check for nil/ID and is the object requested for deleted
If (aObject = nil) Or Not(aObject.ID = -1) Or
Not(FindByID(bDeleted, aObject.ID) = nil) Then
Raise Exception(STR_ORM_ERROR_BUILD);
Dec(bNonCommitedIndex);
aObject.bID := bNonCommitedIndex;
aObject.bORMState := ormAdded;
bNonCommited.Add(aObject);
bModified := TRUE;
End;
Function BManagerClass.RequestChange(Const aObject: BObjectClass): BObjectClass;
Begin
// Check for nil/ID and is the object requested for change/deleted
If (aObject=nil) Or (aObject.ID = -1) Or
Not(FindByID(bDeleted, aObject.ID) = nil) Then
Raise Exception(STR_ORM_ERROR_CHANGE);
Result := FindByID(bNonCommited, aObject.ID);
If Result = nil Then
Begin
If Not(aObject.ORMState = ormCommited) Then
Begin
Raise Exception(STR_ORM_ERROR_CHANGE);
Exit;
End;
Result := aObject.BuildClone;
Result.bORMState := ormChanged;
bNonCommited.Add(Result);
bModified := TRUE;
End;
End;
Procedure BManagerClass.RequestDelete(Const aObject: BObjectClass);
Var
aClone: BObjectClass;
Begin
// Check for nil/ID and is the object requested for change/deleted
If (aObject = nil) Or (aObject.ID = -1) Or
Not(FindByID(bDeleted, aObject.ID) = nil) Then
Raise Exception(STR_ORM_ERROR_CHANGE);
If FindByID(bDeleted, aObject.ID) = nil Then
Begin
aClone := aObject.BuildClone;
aClone.bORMState := ormDeleted;
bDeleted.Add(aClone);
bModified := TRUE;
End;
End;
Function BManagerClass.RequestObject(Const aID: Integer): BObjectClass;
Begin
Result := nil;
If bModified Then
Begin
If Not(FindByID(bDeleted, aID) = nil) Then Exit;
Result := FindByID(bNonCommited, aID);
End;
If Result = nil Then Result := FindByID(bManaged, aID);
End;
Function BManagerClass.GetJSON: TStringList;
Var
i: Integer;
aManaged, aNonCommited, aDeleted: TList;
aObject, aNonCommitedObject: BObjectClass;
Begin
Result := TStringList.Create;
aManaged := bManaged.LockList;
aNonCommited := bNonCommited.LockList;
aDeleted := bDeleted.LockList;
For i := 0 To aManaged.Count - 1 Do
Begin
aObject := BObjectClass(aManaged[i]);
If bModified Then
Begin
If Not(FindByID(aDeleted, aObject.ID) = nil) Then Continue;
aNonCommitedObject := FindByID(aNonCommited, aObject.ID);
If Not(aNonCommitedObject = nil) Then aObject := aNonCommitedObject;
End;
Result.Add(aObject.JSON);
End;
bManaged.UnlockList;
bDeleted.UnlockList;
bNonCommited.UnlockList;
End;
Function BManagerClass.Extract(aCommaSeparatedObjects: String): TList;
Var
lol: String;
aID, aIndex: Integer;
aObject: BObjectClass;
Begin
Result := TList.Create;
While Not(aCommaSeparatedObjects = '') Do
Begin
aIndex := Pos(',', aCommaSeparatedObjects);
If aIndex = 0 Then
Begin
lol := aCommaSeparatedObjects;
aCommaSeparatedObjects := '';
End
Else
Begin
lol := Copy(aCommaSeparatedObjects, 1, aIndex - 1);
Delete(aCommaSeparatedObjects,1, Pos(',', aIndex));
End;
aID := StrToIntDef(lol, -1);
If aID = -1 Then Continue;
aObject := RequestObject(aID);
If Not(aObject = nil) Then Result.Add(aObject);
End;
End;
Function BManagerClass.GetNodeJSON(Const aParentObject:BTreeObjectClass):String;
Var
i: Integer;
aObject: BTreeObjectClass;
aChildren, aManaged: TList;
Begin
Result := '';
aChildren := TList.Create;
If aParentObject = nil Then
Begin
aManaged := bManaged.LockList;
For i := 0 To aManaged.Count - 1 Do
If BTreeObjectClass(aManaged[i]).bParent = nil Then
aChildren.Add(aManaged[i]);
bManaged.UnlockList;
End
Else
Begin
SwapLists(aParentObject.bChildren.LockList, aChildren);
aParentObject.bChildren.UnlockList;
End;
For i := aChildren.Count - 1 DownTo 0 Do
Begin
aObject := BTreeObjectClass(aChildren[i]);
CheckObject(aObject);
If aObject = nil Then aChildren.Delete(i)
Else aChildren[i] := aObject;
End;
Result := '';
For i := 0 To aChildren.Count - 1 Do
Begin
If Not(Result = '') Then Result += ',';
Result += Format('{%s}', [BTreeObjectClass(aChildren[i]).NodeJSON]);
End;
Result := Format('[%s]', [Result]);
End;
Procedure BManagerClass.Commit;
Var
i, aIndex: Integer;
aAllRight: Boolean;
aQuery: BQueryClass;
aDeleted, aNonComitted, aManaged: TList;
aObject, aDeletedObject, aManagedObject: BObjectClass;
Begin
If Not(bModified) Then Exit;
aQuery := BQueryClass.Build(bDBIndex);
aDeleted := bDeleted.LockList;
aNonComitted := bNonCommited.LockList;
aManaged := bManaged.LockList;
// Attempt to process all deletions
For i := 0 To aDeleted.Count - 1 Do
Begin
// If ID negative then skip - it should me remove at NonCommited check
If BObjectClass(aDeleted[i]).ID > DEFAULT_ID Then
aAllRight := BObjectClass(aDeleted[i]).Delete(aQuery);
If Not(aAllRight) Then Break;
End;
// Attempt to process all NonCommited Objects
For i := 0 To aNonComitted.Count - 1 Do
If FindByID(bDeleted, BObjectClass(aNonComitted[i]).ID) = nil Then
Begin
aAllRight := BObjectClass(aNonComitted[i]).Save(aQuery);
If Not(aAllRight) Then Break;
End;
aQuery.Go;
aQuery.Burn;
// Clean up NonCommited and Managed Lists for Deleted Items
For i := 0 To aDeleted.Count - 1 Do
Begin
aDeletedObject := BObjectClass(aDeleted[i]);
aNonComitted.Remove(aDeletedObject);
aObject := FindByID(aNonComitted, aDeletedObject.ID);
If Not(aObject = nil) Then
Begin
aNonComitted.Remove(aObject);
aObject.Burn;
End;
aManaged.Remove(aDeletedObject);
aObject := FindByID(aManaged, aDeletedObject.ID);
If Not(aObject = nil) Then
Begin
aManaged.Remove(aObject);
aObject.Burn;
End;
End;
// Apply changes of NonCommited Items to Managed Items
For i := 0 To aNonComitted.Count - 1 Do
Begin
aObject := BObjectClass(aNonComitted[i]);
Case aObject.ORMState Of
ormChanged:
Begin
aManagedObject := FindByID(aManaged, aObject.ID);
If Not(aManagedObject = nil) Then aManagedObject.Load(aObject);
aObject.Burn;
ProcessCommit(aManagedObject, ormChanged);
End;
ormAdded:
Begin
aObject.bORMState := ormCommited;
aManaged.Add(aObject);
ProcessCommit(aObject, ormAdded);
End;
End;
End;
For i := 0 To aDeleted.Count - 1 Do
BObjectClass(aDeleted[i]).Burn;
aDeleted.Clear;
aNonComitted.Clear;
bManaged.UnlockList;
bDeleted.UnlockList;
bNonCommited.UnlockList;
bModified := FALSE;
End;
Constructor BManagerClass.Build(Const aManagedList: TThreadList;
Const aDBIndex: Integer);
Begin
bModified := FALSE;
bDBIndex := aDBIndex;
bManaged := aManagedList;
bDeleted := TThreadList.Create;
bNonCommited := TThreadList.Create;
bNonCommitedIndex := -1;
End;
Destructor BManagerClass.Burn;
Begin
PurgeList(bNonCommited);
PurgeList(bDeleted);
End;
End.