Série da semana: Primeiros passos no Angular

Veja mais

Insert,Update em mais de uma tabela na MESMA operação(botão) (rollback em todas, caso um deles der erro!)

12/03/2019

20

Bom dia a todos.
Minha questão é simples mas preciso de uma resposta com exemplos e bem prática.
suponha que em uma determinada operação (ao clicar no botão "GRAVAR" por exemplo) eu faça um insert em uma tabela, e dois updates em outras duas tabelas. na mesma operação.
Como assegurar um rollback caso a operação venha a falhar em uma das 3 tabelas? Eu tenho uma vaga idéia de colocar um starttransaction no inicio da operacao mas nao sei como fazer...
Esta resposta ficará para consultas futuras, pois nao achei nada parecido no fórum.
Obrigado a quem colaborar.
Responder

Posts

Olá Emerson, tudo bem?

Você precisa utilizar os métodos específicos dos seus componentes de acesso a dados para abrir uma transação com o banco.

Com DBExpress funciona assim?

var
   FIsolationLevel : TDBXIsolations;
   FTransaction : TDBXTransactions;   
begin
//Abrir Transação
   FIsolationLevel := TDBXIsolations.Create;
   FTransaction := DMConnection.Conn1.BeginTransaction(FIsolationLevel.ReadCommitted); 
   //guarde esta variável, ela é o id da transação e será necessária para comitar ou rollback;

//usar normalmente os componentes...

ClientDataSet.pos;
ClientDataSet.applyUpdates(-1)
sqlQuery.execute;
//... todos os tipos de operações utilizando componentes de acesso a dados e queries;


try
   Conn1.CommitFreeAndNil(FTransaction);
except
     Conn1.RollbackFreeAndNil(FTransaction);
end;




Espero ter ajudado, um forte abraço e fique com Deus.
Responder

26/03/2019

Emerson

Obrigado pela resposta Hélio, perdoe-me, esqueci de informar, estou usando XE6 banco Mysql e componente Firedac.
veja abaixo parte do codigo em que duas tabelas sao afetadas na mesma operação. Como abraçar duas ou mais tabelas com as transactions ??
De modo que se uma delas dê erro seja feito rollback em todas ???
poderia me dar um exemplo utilizando o firedac /mysql ? Agradeço a quem ajudar.

// primeira tabela
QyAux.SQL.Clear;
QyAux.SQL.Add('INSERT INTO OS_andamento');
QyAux.SQL.Add('(OS,datahora,status)');
QyAux.SQL.Add('VALUES(:pOs, :pagora, :pstatus)');

QyAux.ParamByName('pos').AsString := edtBarCode.Text;
QyAux.ParamByName('pstatus').AsString := 'IS';
QyAux.ParamByName('pagora').AsDateTime :=Agora;
try
FrmMenuPrincipal.FDConnection1.txoptions.AutoCommit := false;
QyAux.ExecSQL;
FrmMenuPrincipal.FDConnection1.Commit;
FrmMenuPrincipal.FDConnection1.txoptions.AutoCommit := true;
except
on E: Exception do
begin
FrmMenuPrincipal.FDConnection1.Rollback;
FrmMenuPrincipal.FDConnection1.txoptions.AutoCommit := true;
showmessage('E R R O ! Repetir esta operação.' + #13 + #10 + stringOfChar('=', 65) + #13 + #10 + 'Apresenta Cartao' + #13 + #10 +
QyAux.SQL.Text + #13 + #10 + E.Message);
GravarLog('Apresenta Cartao' + #13 + #10 + QyAux.SQL.Text + #13 + #10 + E.Message);
abort;
end;
end;
// segunda tabela
QyAux.SQL.Clear;
QyAux.SQL.Text := 'Update OS set os_abertoem = :pagora,os_posicao=:pstatus ' + ' where idOS=' +QuotedStr(edtBarCode.Text);
QyAux.ParamByName('pagora').AsDateTime :=Agora;
QyAux.ParamByName('pstatus').AsString :='IS';

try
FrmMenuPrincipal.FDConnection1.txoptions.AutoCommit := false;
QyAux.ExecSQL;
FrmMenuPrincipal.FDConnection1.Commit;
FrmMenuPrincipal.FDConnection1.txoptions.AutoCommit := true;
except
on E: Exception do
begin
FrmMenuPrincipal.FDConnection1.Rollback;
FrmMenuPrincipal.FDConnection1.txoptions.AutoCommit := true;
showmessage('E R R O ! Repetir esta operação.' + #13 + #10 +
stringOfChar('=', 65) + #13 + #10 + 'Apresenta Cartao' + #13 + #10 +
QyAux.SQL.Text + #13 + #10 + E.Message);
GravarLog('Apresenta Cartao' + #13 + #10 + QyAux.SQL.Text + #13 + #10 +
E.Message);
abort;
end;
end;
Responder
Olá novamente Emerson,

Desculpe a demora em responder...

Você precisa iniciar a transação com FDConnection1.StartTransaction e pode dando post normalmente. deixe o commit para o final. Todas as Queryes devem estar no mesmo bloco Try except com um FDConnection1.Commit antes do Except e não separadas.

Dentro do Except você deve fazer "ifs" para testar qual sql gerou o erro para mostrar o usuário, e em seguida dar FDConnection1.Rollback;

Espero ter ajudado.

Qualquer coisa, poste como ficou o novo código e avançaremos no seu problema!

Um forte abraço e fique com Deus.!

Responder

17/04/2019

Emerson

Bom dia Hélio!
somente agora consegui retomar esta questão.
acredito que ficará assim:
...
FDConnection1.StartTransaction;
try
QYSs.ExecSQL;
QYIntd.ExecSQL;
QYVeic.ExecSQL;
FrmMenuPrincipal.FDConnection1.Commit;
except
on E: Exception do
begin
FrmMenuPrincipal.FDConnection1.Rollback;

mas... vc disse que eu vou colocar ifs para saber qual tabela deu erro... mas ao chegar nesta excessao, como eu vou saber quais das tabelas acima (QYSs,QYIntd ou QYVeic).... ocasionou a excessao ???

Obrigado pela atenção.
Responder

30/04/2019

Emerson

FIREDAC - DELPHI -STARTTRANSACTION
==================================
Bem, parece que está funcionando bem com sua dica, vou deixar postado aqui para futuras consultas.
SEMPRE QUE FOR DAR INSERT /UPDATE /DELETE em uma MESMA OPERACAO, deverá alimentar os FDQUERY (um para cada tabela)
Note que "alimentamos as FDQUERY sem dar o execsql...

// Insert OS_andamento...
QYAndamento.SQL.Clear;
QYAndamento.SQL.Add('INSERT INTO OS_andamento');
QYAndamento.SQL.Add('(OS,datahora,status,idFunc)');
QYAndamento.SQL.Add('VALUES(:pOs, :pagora, :pstatus,:pFuncionario)');

QYAndamento.ParamByName('pos').AsString := edtBarCode.Text;
QYAndamento.ParamByName('pstatus').AsString := 'IS';
QYAndamento.ParamByName('pagora').AsDateTime :=now();
QYAndamento.ParamByName('pFuncionario').AsString :=Mmecanico;

// Update em OS
QYOS.SQL.Clear;
QYOS.SQL.Text := 'Update OS set os_abertoem = :pagora,os_posicao=:pstatus ' + ' where idOS=' +QuotedStr(edtBarCode.Text);
QYOS.ParamByName('pagora').AsDateTime :=now();
QYOS.ParamByName('pstatus').AsString :='IS';

// Update em Funcionario para marcar O.S. e veiculo em aberto...
QyFunc.SQL.Clear;
QyFunc.SQL.Text := 'Update Funcionario set os_aberto = :pNumOs,os_veiculo=:pveiculo ' + ' where idFuncionario=:pFuncionario';
QyFunc.ParamByName('pNumOs').AsInteger :=StrToInt(edtBarCode.Text);
QyFunc.ParamByName('pveiculo').AsString :=mVeiculo;
QyFunc.ParamByName('pFuncionario').AsString :=mmecanico;

//Agora sim damos o execsql em todos juntos, se algum falhar toda as alteracoes sao abortadas... (rollback)
FrmMenuPrincipal.FDConnection1.StartTransaction;
try
QYAndamento.ExecSQL;
QYOS.ExecSQL;
QyFunc.ExecSQL;
FrmMenuPrincipal.FDConnection1.Commit;
except
on E: Exception do
begin
FrmMenuPrincipal.FDConnection1.Rollback;
showmessage('E R R O ! Repetir esta operação.' + #13 + #10 +stringOfChar('=', 65) + #13 + #10
+ 'Apresenta Cartao' + #13 + #10 +QyAux.SQL.Text + #13 + #10 + E.Message);
abort;
end;
end;
Application.MessageBox('OPERACAO REALIZADA COM SUCESSO...,'Aviso',mb_Ok+MB_ICONINFORMATION);

end;
Responder