我使用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;
答案 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}}将不再适用。