Transação Firebird DBExpress

Delphi

29/03/2006

Pessoal estou com um problema na utilização de transação em firebird e DBExpress.

Seguinte, tenho que gravar informações em duas tabelas, e caso dê algum problema, não gravar em nenhuma.
Então faço o seguinte.

No ClientDataSet1 os registros já estão inseridos e ´Post´.
Então:

InicioTransacao.
try
  ClientDataSet1.ApplyUpdates(-1);
  ClientDataSet2.Insert;
  PreencheClientDataSet2;
  ClientDataSet2.Post;
  ClientDataSet2.ApplyUpdates(-1);
  Commit;
except
  RoolBack;
end;

problema é que os dados do primeiro client são gravados no banco, mesmo estando dentro da transação o apply;


Andremuller

Andremuller

Curtidas 0

Respostas

Vinicius2k

Vinicius2k

29/03/2006

Colega,

O método ApplyUpdates do TClientDataSet nunca gera exceção. Você precisa ´amarrar´ o Commit ou Rollback da transação ao número de erros ocorridos. Um exemplo:
{ ... }
var
  TudoOK: Boolean;
  Transacao: TTransactionDesc; 
begin
  try
    { **** Iniciando a transação **** }
    Transacao.TransactionID:= 1;
    Transacao.IsolationLevel:=  xilReadCommitted;
    SeuSQLConnection.StartTransaction(Transacao);

    { **** Aplicando os updates **** }
    if ClientDataSet1.ApplyUpdates(0) = 0 then
    begin
      ClientDataSet2.Insert;
      PreencheClientDataSet2;
      ClientDataSet2.Post;
      if ClientDataSet2.ApplyUpdates(0) = 0 then
        TudoOK:= True;
      // TudoOK só será true se não houver erro em nenhum Update
    end;

    { **** Confirmando ou voltando a transação **** }
    if TudoOK then
      SeuSQLConnection.Commit(Transacao)
    else
      SeuSQLConnection.Rollback(Transacao);
  except
    // Ocorreu uma exceção qualquer no bloco ...
    SeuSQLConnection.Rollback(Transacao);
  end;
end;


Outras dúvidas sobre transação com dbExpress podem ser esclarecidas usando a ferramenta de pesquisa do fórum.
Como exemplo, este tópico:
http://forum.clubedelphi.net/viewtopic.php?t=58547


GOSTEI 0
Rodolfo.pirolo

Rodolfo.pirolo

29/03/2006

Só pegando uma carono no problema alheio.

Digamos que estes dois clientdatsets referem-se a pedido e itens do pedido. Até aí beleza, só que preciso no momento da gravação destes dar baixa do meu estoque.

Tem como fazer utilizando esta mesma estrutura?

Atenciosamente,

Rodolfo


GOSTEI 0
Adriano Santos

Adriano Santos

29/03/2006

Só pegando uma carono no problema alheio. Digamos que estes dois clientdatsets referem-se a pedido e itens do pedido. Até aí beleza, só que preciso no momento da gravação destes dar baixa do meu estoque. Tem como fazer utilizando esta mesma estrutura? Atenciosamente, Rodolfo


Claro [b:1305852a2c]rodolfo.pirolo[/b:1305852a2c], como o [b:1305852a2c]Vinicius2K[/b:1305852a2c] disse:

Você precisa ´amarrar´ o Commit ou Rollback da transação ao número de erros ocorridos.


No momento em que estiver gravando informações nos dois ClientDataSet´s você também pode elaborar uma rotina para gravar no Estoque, ou seja, dar o UpplyUpdates nas tabelas de estoque e testar se estes apply´s foram bem sucedidos, veja no exemplo do [b:1305852a2c]Vinicius2k[/b:1305852a2c] em outro tópico:

var
  TudoOK: Boolean;
  Transacao: TTransactionDesc;
begin
  TudoOK:= False;
  Transacao.TransactionID:= 1;
  Transacao.IsolationLevel:=  xilReadCommitted;
  SQLConnection1.StartTransaction(Transacao);
  // Aplicando os updates
  if ClientDataSet1.ApplyUpdates(0) = 0 then
    if ClientDataSet2.ApplyUpdates(0) = 0 then
      if ClientDataSet3.ApplyUpdates(0) = 0 then
        if ClientDataSet4.ApplyUpdates(0) = 0 then
          TudoOK:= True;
  // Note que TudoOK só será true se não houver nenhum erro em nenhum dos Updates
  if TudoOK then
  begin
    SQLConnection1.Commit(Transacao);
    ShowMessage(´Atualizações OK.´);
  end
  else
  begin
    SQLConnection1.Rollback(Transacao);
    ShowMessage(´Ocorreram erros. Alterações descartadas.´);
  end;
end;


Aki o TudoOK só ficará True se todos os ApplyUpdates forem OK.
Tô certo [b:1305852a2c]Vinicius[/b:1305852a2c]???


GOSTEI 0
Rodolfo.pirolo

Rodolfo.pirolo

29/03/2006

Adriano,

Desculpe-me, acho que não fui claro na minha questão.

Minha dúvida é a seguinte:

Gostaria de montar uma atualização desta maneira:

...
if ClientDataSet2.ApplyUpdates(0) = 0 then
Para cada item do meu CDS_Item_pedidos
update estoque set qtde_estoque = qtde_estoque - qtde_vendida
ApplyUpdates

Se tudo terminar bem, commit
senão rollback em tudo.

Isto é possivel? Ainda não sei como atualizar o estoque com applyupadates, porque para esta rotina estou usando Store Procedure. Talvez criar um cds para atualizá-lo.

Atenicosamente,

Rodolfo


GOSTEI 0
Renatacoimbra

Renatacoimbra

29/03/2006

Entrando no assunto.

usando o Exemplo do colega Vinicius2K, basta vc dar baixa no seu estoque e só gravar em memoria usando o Post.

depois vc usa o ApplyUpdate, e se o ApplyUpdate for bem sucedido vc comita.


[]´s


GOSTEI 0
Vinicius2k

Vinicius2k

29/03/2006

Colegas,

Particularmente, eu prefiro manter a baixa de estoque como uma trigger ´After Insert´ na tabela de ítens. É mais prático do que Stored Procedure, na minha opinião.

Porém, se você não desejar ou não puder deixar esta regra do negócio no SGBD. Pode implementar algo parecido com isto:
var
  TudoOK: Boolean;
  Transacao: TTransactionDesc;
  I: Integer;
begin
  TudoOK:= False;
  
  // Definindo instrução para o update do estoque
  SQLDataSet1.CommandText := ´update ESTOQUE set QTDE = QTDE - :venda where IDPRODUTO = :idproduto´;

  Transacao.TransactionID:= 1;
  Transacao.IsolationLevel:=  xilReadCommitted;
  SQLConnection1.StartTransaction(Transacao);

  // Aplicando os updates
  if ClientDataSet1.ApplyUpdates(0) = 0 then
  begin
    if ClientDataSet2.ApplyUpdates(0) = 0 then
    begin
      try
        for I := 0 to ClientDataSet2.RecordCount - 1 do
        begin
          // Atualizando o estoque.
          SQLDataSet1.ParamByName(´venda´).AsInteger := 
            ClientDataSet2.FieldByName(´qtde´).AsInteger;
          SQLDataSet1.ParamByName(´idproduto´).AsInteger := 
            ClientDataSet2.FieldByName(´idproduto´).AsInteger;
          SQLDataSet1.ExecSQL;         
        end;
        TudoOK := True;  
      except
        // Erro ao atualizar o estoque
        TudoOK := False;
      end;
    end;
  end;
  // Note que TudoOK só será true se não houver nenhum erro em nenhum dos Updates
  if TudoOK then
  begin
    SQLConnection1.Commit(Transacao);
    ShowMessage(´Atualizações OK.´);
  end
  else
  begin
    SQLConnection1.Rollback(Transacao);
    ShowMessage(´Ocorreram erros. Alterações descartadas.´);
  end;
end;



GOSTEI 0
Rodolfo.pirolo

Rodolfo.pirolo

29/03/2006

Vinicius,

Corrija-me se estiver errado, quando mando executar a instrução sql de atualização do estoque conforme voce demonstrou, caso der um erro, quando executar o rollback, ele não retornará os valores anteriores.

Exemplo:

1° registro: atualizado com sucesso.
2° registro: atualizado com sucesso.
3° registro: erro na atualização.

Neste caso o rollback não alterará os valores do primeiro e segundo lançamentos.

Estou certo ou errado?

Abraços

Rodolfo


GOSTEI 0
Vinicius2k

Vinicius2k

29/03/2006

Nao...

Tudo o que você faz envolto na mesma transação é afetado pelo commit ou rollback.
Você poderia ter centenas de updates que, se eles estiverem todos dentro da mesma transação (que é o nosso caso), ao ser dado o rollback, tudo seria desfeito, incluindo triggers e SPs executadas pelo SGBD.

Fique tranquilo quanto a isso. ;)


GOSTEI 0
Rodolfo.pirolo

Rodolfo.pirolo

29/03/2006

Vinicius

Obrigado, funcionou belezinha.

Só desculpe-me pela demora da resposta, somente hoje pude testar a sua idéia.

Obrigado também ao Adriano Santos e a Renata Coimbra.

Abraços,

Rodolfo


GOSTEI 0
POSTAR