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

12/03/2019

0

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

Emerson

Responder

Posts

13/03/2019

Hélio Devmedia

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

31/03/2019

Hélio Devmedia

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

Assista grátis a nossa aula inaugural

Assitir aula

Saiba por que programar é uma questão de
sobrevivência e como aprender sem riscos

Assistir agora

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar