implementing a stack of commands for obtaining a transactional behaviour- Delphi
I need to create a tool for performing complex scripts against a database.
For several resasons I cannot rely on DB transactional behaviour, but I need to implement my own transactional system.
The approach I am trying is with the help of the command pattern (my case is more complex, here I put a simplified version for discussion):
type
IMyCommand = interface(IInterface)
procedure Execute();
procedure Undo();
end;
type
TSQLCommand = class (TInterfacedObject, IMyCommand)
private
FDBConnection: TDBConnection;
FDBQuery: TDBQuery;
FExecuteSQL: string;
FUndoSQL: string;
FExecuted: boolean; // set to True as the command has been executed
public
procedure Execute;
procedure Undo;
procedure Prepare(aExecuteSQL, aUndoSQL: string);
constructor Create(aDBConnection: TDBConnection);
destructor Destroy; override;
end;
I create a set of actions, for every action I will pass a "Execute" and "Undo" sql statement, examples:
A call to Prepare could be:
Prepare('INSERT INTO TESTTABLE (ID, DATA) VALUES (15, 'Hello')',// aExecuteSQL
'DELETE FROM TESTTABLE WHERE ID = 15'); //aUndoSQL
so somehow I am making very small changes (like inserting a single simple row, updating a single row, ...), for every change the "undo" is very obvious.
I will prepare a stack of command objects (using probably the TObjectStack collection), and call the Execute method one command at a time and as one is executed I will set FExecuted to True, and save the component to disk.
So what I whant to do is to run all the scripts, but I want to manage the cases in which something goes wrong.
If something goes wrong I would like to execute all the commands from last to first calling the Undo method. Of course before doing this I need to be able to restore from disk the components (in case the failure is an hardware failure, in case the failure is another reason I already have the stack in memory and I can easily call undo one command at a time).
Note: The main reason why I cannot rely on the DB transactional behaviour is that I need to insert also big blobs, and every blob is downloaded from internet and then inserted, so I cannot leave a transaction open for ever because I want to commit every small change to the db. What I do开发者_C百科 with blobs is download one, insert it, download next, insert it, ...
So my question is: could you suggest a way to persist to disk my objects? I have Delphi 2009, so one option is to make a TInterdacedPersistent and save the component to stream and then to file, anyway in this way I would have many files, with extra complicatinos, while I would prefer a single file. Could you suggest?
Edit: I realized TObjectStack is buggy in Delphi 2009 (Pop doesn't return a correct type), so the same can be done with TObjectStack.
I can't see a better approach than using a transaction, as Andrei K. mentioned your implementation is NOT safe, therefore using StartTransaction, Commit and Rollback is a MUST!
精彩评论