Delphi x PostgreSQL - Transação encerra após erro
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.
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
Curtidas 0
Respostas
Washington Somensi
02/09/2013
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.
GOSTEI 0
Douglas Junior
02/09/2013
Acabamos resolvendo o problema estendendo o componente do UNIDAC e implementando o controle da transação com Savepoint.
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
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
GOSTEI 0
Alessandro Yamasaki
02/09/2013
Excelente dica.
GOSTEI 0
José
02/09/2013
Obrigado amigo Douglas pro compartilhar a solução o seu problema conosco, sendo assim estou dando o tópico por concluído.
GOSTEI 0
Douglas Junior
02/09/2013
Obrigado José, confesso que procurei como editar o tópico e colocar como [RESOLVIDO], mas não encontrei a opção.
Abraços.
Abraços.
GOSTEI 0
José
02/09/2013
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.
Obrigado e precisando estamos a disposição para ajudar.
GOSTEI 0