Question

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 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.

Was it helpful?

Solution

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!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top