如何正确启动,工作和完成交易?

时间:2014-10-03 15:12:17

标签: delphi dbexpress

我使用MySQL,我知道不允许嵌套连接 - 使用"保存点"为此 - 但我想创建一个更通用的代码,也可以与其他DBMS一起使用。

所以,我想知道如何在下面的代码中正确启动,工作和完成交易?

一旦ExampleDAO.Save()函数可以在其他函数中使用,例如OtherExampleDAO.Save(),我需要在尝试启动一个新函数之前验证事务是否已经启动。

验证if Assigned(dbTransaction) then的行始终返回true,那么如何正确验证dbTransaction是否已实例化?

function TExampleDAO.Save(const Example: TExample): Boolean;
var
  dbxTransaction: TDBXTransaction;
begin
  if Assigned(Example) then // prevents invalid object, like ExampleDAO.Save(nil);
  begin
    try
      if (_connection.TransactionsSupported) AND
        ((not _connection.InTransaction) OR (_connection.MultipleTransactionsSupported)) then
      begin
        dbxTransaction := _connection.BeginTransaction(TDBXIsolations.ReadCommitted);
      end;

      try
        // example
        _sqlQuery.Close;
        _sqlQuery.SQL.Clear;
        _sqlQuery.SQL.Add('INSERT INTO example(a, b) '
                        + 'VALUES(:a, :b)');
        _sqlQuery.ParamByName('a').AsAnsiString := Example.A;
        _sqlQuery.ParamByName('b').AsDateTime := Example.B;
        _sqlQuery.ExecSQL(False);

        // example info
        _sqlQuery.Close;
        _sqlQuery.SQL.Clear;
        _sqlQuery.SQL.Add('INSERT INTO example_info(c, d) '
                        + 'VALUES(:c, :d)');
        _sqlQuery.ParamByName('c').AsInteger := Example.Info.C;
        _sqlQuery.ParamByName('d').AsFloat := Example.Info.D;
        _sqlQuery.ExecSQL(False);

        if Assigned(dbxTransaction) then
          _connection.CommitFreeAndNil(dbxTransaction);

        Result := True;
      except
        on Exc:Exception do
        begin
          if Assigned(dbxTransaction) then
            _connection.RollBackFreeAndNil(dbxTransaction);

          raise Exc;
          Result := False;
        end;
      end;
    finally
      if Assigned(dbxTransaction) then
        FreeAndNil(dbxTransaction);
    end;    
  end
  else
  begin
    Result := False;
  end;
end;

1 个答案:

答案 0 :(得分:1)

您需要在功能开始时将dbxTransaction正确初始化为nil。 Delphi中的局部变量(至少在Win32平台上)在为其分配值之前不会初始化,这意味着内容未知。将nil以外的任何值传递给Assigned都会产生True。我建议永远不要在任何平台上测试本地变量的内容,直到它在您的代码中分配了值。

以下是如何使其工作的示例。 (我还在异常块中删除了对Result的不必要的赋值。)

function TExampleDAO.Salve(const Example: TExample): Boolean;
var
  dbxTransaction: TDBXTransaction;
begin
  dbxTransaction := nil;            // Initialize the transaction variable here

  if Assigned(Example) then // prevents invalid object, like ExampleDAO.Save(nil);
  begin
    try
      if (_connection.TransactionsSupported) AND
        ((not _connection.InTransaction) OR (_connection.MultipleTransactionsSupported)) then
      begin
        dbxTransaction := _connection.BeginTransaction(TDBXIsolations.ReadCommitted);
      end;

      try
        // example
        _sqlQuery.Close;
        _sqlQuery.SQL.Clear;
        _sqlQuery.SQL.Add('INSERT INTO example(a, b) '
                        + 'VALUES(:a, :b)');
        _sqlQuery.ParamByName('a').AsAnsiString := Example.A;
        _sqlQuery.ParamByName('b').AsDateTime := Example.B;
        _sqlQuery.ExecSQL(False);

        // example info
        _sqlQuery.Close;
        _sqlQuery.SQL.Clear;
        _sqlQuery.SQL.Add('INSERT INTO example_info(c, d) '
                        + 'VALUES(:c, :d)');
        _sqlQuery.ParamByName('c').AsInteger := Example.Info.C;
        _sqlQuery.ParamByName('d').AsFloat := Example.Info.D;
        _sqlQuery.ExecSQL(False);

        if Assigned(dbxTransaction) then
          _connection.CommitFreeAndNil(dbxTransaction);

        Result := True;
      except
        on Exc:Exception do
        begin
          if Assigned(dbxTransaction) then
            _connection.RollBackFreeAndNil(dbxTransaction);

          raise Exc;
        end;
      end;
    finally
      if Assigned(dbxTransaction) then
        FreeAndNil(dbxTransaction);
    end;    
  end
  else
  begin
    Result := False;
  end;
end;

正如@SirRufo在您的问题的评论中指出的那样,未能将Example作为参数传递也可能引发异常,这意味着它可能成为一个过程而不是一个函数而{ {1}}将不再适用。