Fórum Delphi x PostgreSQL - Transação encerra após erro #453905

02/09/2013

0

Ola Pessoal

Estamos começando a utilizar o PostgreSQL 9 no Delphi, e estamos passando por algumas dificuldades ao se tratar das transações.

Estamos utilizando o conjunto de datasets UniDAC, mas acredito que este mesmo problema deva ocorrer em outros como Zeos.

O problema é o seguinte:

O PostgreSQL por padrão utiliza "AUTOCOMMIT ON" em suas transações, ou seja, ao executar o "POST" os dados são persistidos no banco de dados automaticamente. Para que isso não ocorra precisamos antes executar um "START TRANSACTION", em seguida executar os Inserts, Updates e Deletes, e no final executar o "COMMIT" ou "ROLLBACK".

Até este ponto tudo bem, dessa forma conseguimos utilizar o banco de dados semelhante ao que utilizamos anteriormente no Oracle, ou seja, os dados só são persistidos no banco após executar o COMMIT explicitamente.

A questão é que: após dar o "START TRANSACTION", se algum erro ocorrer ao executar o "POST" a transação atual é "encerrada" automaticamente, impedindo que seja executado "POST" novamente. Ao tentar dar o "POST" novamente recebemos o erro: "transação atual foi interrompida, comandos ignorados até o fim do bloco de transação"

Isso nos obriga a executar um "ROOLBACK" após o erro, então executar um "START TRANSACTION" novamente, e finalmente estamos aptos a executar o "POST" novamente.

Exemplo de situação:
Imagine que você vai cadastrar um produto, então é executado um "START TRANSACTION" e um "INSERT" no dataset de produto, você começa a inserir os dados, grava o produto com o "POST" e tudo ocorre bem. Em seguida começa a cadastrar as derivações desse produto (tudo na mesma TRANSACTION, sem dar COMMIT), e na hora de dar o "POST" no dataset de derivações ocorre um erro de campo requerido, a partir deste ponto já perdemos a transação atual, não podemos simplesmente preencher o campo requerido e dar "POST" novamente, pois ocorre o erro citado acima, somos obrigados a dar "ROLLBACK" e "START TRANSACTION" novamente, e perder o "POST" que foi executado com sucesso no dataset de produtos.

Gostaria de saber se existe alguma forma de contornar esse problema, pois quando estamos em um formulário grande com vários datasets, não podemos simplesmente dar "ROLLBACK" quando um erro ocorrer, pois com isso perdemos todos os "POST" executados em outros datasets que tiveram sucesso.

Agradeço antecipadamente

Abraços.
Douglas Junior

Douglas Junior

Responder

Posts

03/09/2013

Washington Somensi

Ola Douglas, eu utilizo o Zeos e não tenho esse tipo de problema, quando ocorre erro ao gravar, consigo corrigir e gravar novamente, desde que o post esteja entre de um Try .. Except.
Responder

Gostei + 0

03/09/2013

Douglas Junior

Acabamos resolvendo o problema estendendo o componente do UNIDAC e implementando o controle da transação com Savepoint.

unit AJUniQuery;

interface

uses
  System.SysUtils, System.Classes, Data.DB, MemDS, DBAccess, Uni;

type
  TAJUniQuery = class(TUniQuery)
  private
    { Private declarations }
    procedure StartTransactionIfNotExists;
  protected
    { Protected declarations }

  public
    { Public declarations }
    procedure Post; override;
    procedure Delete;
  published
    { Published declarations }
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('AJR Controls', [TAJUniQuery]);
end;

{ TAJUniQuery }

procedure TAJUniQuery.Delete;
begin
  StartTransactionIfNotExists;
  try
    Connection.Savepoint('savepoint_ajuniquery');
    inherited;
  except
    on E: Exception do
      begin
        Connection.RollbackToSavepoint('savepoint_ajuniquery');
        raise;
      end;
  end;
end;

procedure TAJUniQuery.Post;
begin
  StartTransactionIfNotExists;
  try
    Connection.Savepoint('savepoint_ajuniquery');
    inherited;
  except
    on E: Exception do
      begin
        Connection.RollbackToSavepoint('savepoint_ajuniquery');
        raise;
      end;
  end;
end;

procedure TAJUniQuery.StartTransactionIfNotExists;
begin
  if not Connection.InTransaction then
    Connection.StartTransaction;
end;

end.


Dessa forma, salvamos o Savepoint antes de executar o POST, e se algum erro ocorrer o Savepoint é restaurando, evitando de perder os processos anteriores que foram executados com sucesso.

Implementamos também um método que inicia uma transação automaticamente caso não existe nenhuma em atividade.


Abraços
Douglas Junior
Responder

Gostei + 0

03/09/2013

Alessandro Yamasaki

Excelente dica.
Responder

Gostei + 0

03/09/2013

José

Obrigado amigo Douglas pro compartilhar a solução o seu problema conosco, sendo assim estou dando o tópico por concluído.
Responder

Gostei + 0

03/09/2013

Douglas Junior

Obrigado José, confesso que procurei como editar o tópico e colocar como [RESOLVIDO], mas não encontrei a opção.

Abraços.
Responder

Gostei + 0

03/09/2013

José

Amigo, apenas nos moderadores podemos finalizar os tópicos, para facilitar para nós pedimos aos usuários que apenas sinalizem que sua duvida foi solucionada.
Obrigado e precisando estamos a disposição para ajudar.
Responder

Gostei + 0

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

Aceitar