2015-07-10 7 views
1

Я пытаюсь обновить две разные таблицы SQL в одном цикле, используя параметризованные запросы в Delphi XE8. Я также хочу обернуть все это в транзакцию, так что если что-либо в цикле не удастся, ни одна таблица не будет обновлена.Множественные параметризованные Delphi SQL-обновления в транзакции

Я действительно не знаю, что я делаю, был бы рад помочь.

Код ниже - это упрощенная версия того, что я пытаюсь достичь, и мое лучшее предположение о том, как это сделать. Но я совсем не уверен в этом, особенно использование двух наборов данных, связанных с компонентом «SQL-соединение».

SQL_transaction.TransactionID :=1; 
SQL_transaction.IsolationLevel:=xilREADCOMMITTED; 
SQL_connection.BeginTransaction; 
Try 
    { Create connections } 

    SQL_dataset1    :=TSQLDataSet.Create(nil); 
    SQL_dataset1.SQLConnection:=SQL_connection; 

    SQL_dataset2    :=TSQLDataSet.Create(nil); 
    SQL_dataset2.SQLConnection:=SQL_connection; 

    { Create queries } 

    SQL_dataset1.CommandType:=ctQuery; 
    SQL_dataset1.CommandText:={ some parameterized query updating table A } 

    SQL_dataset2.CommandType:=ctQuery; 
    SQL_dataset2.CommandText:={ some parameterized query updating table B } 

    { Populate parameters and execute } 

    For I:=0 to whatever do 
    begin 
    SQL_dataset1.ParamByName('Table A Field 1').AsString:='Value'; 
    SQL_dataset1.ExecSQL; 

    SQL_dataset2.ParamByName('Table B Field 1').AsString:='Value'; 
    SQL_dataset2.ExecSQL; 
    end; 

    SQL_connection.Commit(SQL_transaction); 
except 
    SQL_connection.Rollback(SQL_transaction); 
end; 

Я использую Delphi XE8, а база данных может быть либо SQL-сервером, либо SQLite.

+3

У вас есть вопрос здесь? Я не эксперт в Delphi, но это выглядит нормально для меня. –

+1

Код выглядит нормально, один маленький nitpick, вы должны сделать ререйз исключение ... – whosrdaddy

ответ

4

Логика обработки транзакций верна (за исключением отсутствия повторного рейса исключений, упомянутого @whosrdaddy). В чем-то не хватает try..finally блоков для ваших экземпляров набора данных. За исключением того, что вы должны прекратить использование устаревших методов TSQLConnection, которые используют запись TTransactinDesc (всегда проверяйте предупреждения компилятора при создании своего приложения). И вы также можете переключиться на компонент TSQLQuery. Попробуйте что-нибудь подобное вместо этого:

var 
    I: Integer; 
    Query1: TSQLQuery; 
    Query2: TSQLQuery; 
    Connection: TSQLConnection; 
    Transaction: TDBXTransaction; 
begin 
    ... 
    Query1 := TSQLQuery.Create(nil); 
    try 
    Query1.SQLConnection := Connection; 
    Query1.SQL.Text := '...'; 

    Query2 := TSQLQuery.Create(nil); 
    try 
     Query2.SQLConnection := Connection; 
     Query2.SQL.Text := '...'; 

     Transaction := Connection.BeginTransaction; 
     try 
     // fill params here and execute the commands 
     for I := 0 to 42 to 
     begin 
      Query1.ExecSQL; 
      Query2.ExecSQL; 
     end; 
     // commit if everything went right 
     Connection.CommitFreeAndNil(Transaction); 
     except 
     // rollback at failure, and re-raise the exception 
     Connection.RollbackFreeAndNil(Transaction); 
     raise; 
     end; 
    finally 
     Query2.Free; 
    end; 
    finally 
    Query1.Free; 
    end; 
end; 
+0

Если вы инициализируете наборы данных до нуля перед окончательным блоком try, вам нужна только одна попытка, наконец, блокировать, чтобы освободить наборы данных. –

+0

@House, вы все равно должны считать, что освобождение внутреннего отказа не выполняется. – TLama

+0

Бесплатные проверки, чтобы убедиться, что они ноль, прежде чем освободить объект ... если что-то происходит на внутренней ... и внешняя никогда не будет установлена ​​... его ref. будет nil, а вызов на него не будет ничего делать ... –

-1

Я предпочитаю попробовать, наконец, над попробовать, кроме

вот как заставить его работать в попытке окончательно блокировать

var 
    a_Error: boolean; 
begin 
a_Error := True;//set in error state... 
SQL_dataset1 := nil; 
SQL_dataset2 := nil; 
SQL_transaction.TransactionID :=1; 
SQL_transaction.IsolationLevel:=xilREADCOMMITTED; 
SQL_connection.BeginTransaction; 

Try 
    { Create connections } 

    SQL_dataset1    :=TSQLDataSet.Create(nil); 
    SQL_dataset1.SQLConnection:=SQL_connection; 

    SQL_dataset2    :=TSQLDataSet.Create(nil); 
    SQL_dataset2.SQLConnection:=SQL_connection; 

    { Create queries } 

    SQL_dataset1.CommandType:=ctQuery; 
    SQL_dataset1.CommandText:={ some parameterized query updating table A } 

    SQL_dataset2.CommandType:=ctQuery; 
    SQL_dataset2.CommandText:={ some parameterized query updating table B } 

    { Populate parameters and execute } 

    For I:=0 to whatever do 
    begin 
    SQL_dataset1.ParamByName('Table A Field 1').AsString:='Value'; 
    SQL_dataset1.ExecSQL; 

    SQL_dataset2.ParamByName('Table B Field 1').AsString:='Value'; 
    SQL_dataset2.ExecSQL; 
    end; 

    a_Error := False;//if you don't get here you had a problem 
finally 
    if a_Error then 
    SQL_connection.Rollback(SQL_transaction) 
    else 
    SQL_connection.Commit(SQL_transaction); 
    SQL_dataset1.Free; 
    SQL_dataset2.Free; 

end;  
end; 

Я добавил код, как Попробуйте наконец работы с объектами инициализации до нуля

TMyObject = class(TObject) 
    Name: string; 
    end; 

procedure TForm11.Button1Click(Sender: TObject); 
var 
    a_MyObject1, a_MyObject2: TMyObject; 
begin 
    a_MyObject1 := nil; 
    a_MyObject2 := nil; 
    try 
    a_MyObject1 := TMyObject.Create; 
    a_MyObject1.Name := 'Object1'; 
    if Sender = Button1 then  
     raise exception.Create('Object 2 not created'); 
    ShowMessage('We will not see this'); 
    a_MyObject2 := TMyObject.Create; 
    a_MyObject2.Name := 'Object2'; 
    finally 
    a_MyObject2.Free; 
    ShowMessage('We will see this even though we called a_MyObject2.free on a nil object'); 
    a_MyObject1.Free; 
    end; 
end; 
+2

Ой, это противно.Вы используете флаги для преобразования try-finally в try-except. Почему бы вам это сделать? У языка уже есть языковая конструкция, добавленная специально для этого. Это блок try-except, который вы заменяете. – Graymatter

+0

На самом деле, почему это неприятно ... Попробуйте Исключиться, если бы исключения не были нормой, не беспокоиться о повторении исключения ... Вы обрабатываете свои транзакции, хорошо это или плохо ... Вы должны прочитать книгу Дэнни Торпса ... в том, что разработчики приложений очень редко должны писать Try Except ... и должны написать Try finally ... –

+0

Он также правильно справляется с освобождением наборов данных. Это будет быстрее, чем обертывание кода в Try Try Try, за исключением, наконец, окончательного ... Вы должны прочитать страницу 112-114 компонента Delphi Component Design от Danny Thorpe. –