Fórum StartTransaction com ExecuteDirect #346684
25/09/2007
0
Por favor, tirem uma dúvida minha. Quando chamamos o método ExecuteDirect do SQLConnection, para criar tabelas, campos etc, no bando de dados, não podemos fazê-lo em uma transação? pois caso o script tenha algum erro, podemos chamar o rollback.
Por exemplo, estou testando o seguinte:
var TD: TTransactionDesc;
begin
TD.TransactionID := 1;
TD.IsolationLevel := xilREADCOMMITTED;
try
Con.StartTransaction(TD);
Con.ExecuteDirect(´ALTER TABLE ALUGUEL ADD TAXA_FAXINA ´+
´NUMERIC(14,2) DEFAULT 0´);
Con.ExecuteDirect(´UPDATE ALUGUEL SET TAXA_FAXINA = 0´);
Con.Commit(TD);
except
Con.Rollback(TD);
raise;
end;
end;
Quando executo o bloco acima, o comando Update não atualiza o campo (TAXA_FAXINA) criado nos registros para 0.
Se chamo sem o StartTransaction, funciona.
O StartTransaction não pode ser chamado em casos como este?
Se alguém puder ajudar, ficarei muito grato!
Marcelo
Tchelllo
Curtir tópico
+ 0Posts
25/09/2007
Nerdex
Gostei + 0
26/09/2007
Adriano Santos
procedure TForm1.Button1Click(Sender: TObject); var TD : TTransactionDesc; I : Integer; begin SQLDataSet1.Close; ClientDataSet1.Close; SQLConnection1.ExecuteDirect(´ALTER TABLE TABELA1 ADD TESTE NUMERIC(15, 2) DEFAULT 0´); SQLConnection1.ExecuteDirect(´UPDATE TABELA1 SET TESTE = 1´); ClientDataSet1.Open; end;
Vai perceber que funciona. As transações devem ser usadas em outras ocasiões. Recomendo dar uma boa lida nesses tópicos:
[quote:02b2fc9ff1=´Adriano Santos´]Velhinho, dá uma olhada neste tópico que tem uma discussão boa sobre o assunto. Vais entender com certeza.
[url=http://forum.clubedelphi.net/viewtopic.php?t=58547&highlight=sqldataset+++clientdataset]Transações com DbExpress de forma Bidirecional[/url]
E mais algumas coisas sobre DBExpress:
[url=http://forum.clubedelphi.net/viewtopic.php?t=75031&start=0&postdays=0&postorder=asc&highlight=&sid=6c3d12b7f2e7a711939b7662cd0834db]dbExpress Interbase 6.5 Delphi 7.0[/url]
[url=http://forum.clubedelphi.net/viewtopic.php?t=75051]Mestre/Detalhe dbExpress[/url]
:wink: :wink:[/quote:02b2fc9ff1]
Gostei + 0
26/09/2007
Tchelllo
Adriano, estava fazendo alguns testes com o exemplo que coloquei, para ver se a transação realmente fazia o rollback ou não, caso o script tivesse erros, e notei que ele faz o rollback.
no exemplo:
begin
Con.StartTransaction(TD);
Con.ExecuteDirect(´ALTER TABLE ALUGUEL ADD TAXA_FAXINA ´+
´NUMERIC(14,2) DEFAULT 0´);
Con.ExecuteDirect(´UPDATE ALUGUEL SET TAXA_FAXINA = A´);
Con.Commit(TD);
except
Con.Rollback(TD);
raise;
end;
end;
ao invés de no campo criado (TAXA_FAXINA) atualizar com o valor ´0´, coloquei o valor ´A´, que irá gerar uma excessão e vai entrar no except, pois trata-se de um campo numeric.
Ví que a aplicação faz o rollback e o campo e muito menos o UPDATE
não são aplicados no banco.
Caso não inicie a transação, o campo é criado, só o UPDATE que não é feito (isso neste exemplo, que insiro o valor ´A´).
Por isso não entendi quando vc disse que o ExecuteDirect vai aplicar o script independente de commit ou não. Nesse exemplo o rollback funcionou.
O que tb não estou entendendo é que quando aplico o script dentro de uma transação, o campo é criado mas o UPDATE não é feito. Se aplico sem transação o campo e o UPDATE são aplicados com sucesso.
Mais uma vez, obrigado!
Marcelo
Gostei + 0
26/09/2007
Adriano Santos
begin try Con.StartTransaction(TD); Con.ExecuteDirect(´ALTER TABLE ALUGUEL ADD TAXA_FAXINA ´+ ´NUMERIC(14,2) DEFAULT 0´); Con.ExecuteDirect(´UPDATE ALUGUEL SET TAXA_FAXINA = A´); Con.Commit(TD); except Con.Rollback(TD); raise; end; end;
Vamos lá camarada, vamos ver se detalhando mais o processo fica mais fácil de entender.
[list:16d80da6e8]
[*:16d80da6e8]1. O método ExecuteDirect é pra ser usado sozinho, isolado e um comando de cada vez, ou seja, quando você faz o primeiro comando ele deve ser comitado se estiver dentro de uma transação. Se estiver usando em controle de transação o commit será dado automaticamente pelo método. Então pense na sua situação.
[list:16d80da6e8]
[*:16d80da6e8] a. Você mandou executar um [b:16d80da6e8]ALTER TABLE[/b:16d80da6e8].
[*:16d80da6e8] b. Sem comitar manou efetuar um [b:16d80da6e8]UPDATE[/b:16d80da6e8] no campo incluso.
[/list:u:16d80da6e8]
O que acontece nesse caso?
Erro. Como o [b:16d80da6e8]ALTER TABLE[/b:16d80da6e8] ainda não foi aplicado por causa do commit que não foi dado, o campo XXXX não existe na base.
Se estiver usando o [b:16d80da6e8]ExecuteDirect[/b:16d80da6e8] sem controle de transação, o seu [b:16d80da6e8]ALTER TABLE[/b:16d80da6e8] será aplicado automaticamente e o segundo comando não falhará.
[*:16d80da6e8] 2. Por outro lado se você aplicar o commit a cada situação, ou seja, a cada comando recebido pelo [b:16d80da6e8]ExecuteDirect[/b:16d80da6e8] você terá sucesso, pois a segunda vez que o [b:16d80da6e8]ExecuteDirect[/b:16d80da6e8] for chamado o seu [b:16d80da6e8]ALTER TABLE[/b:16d80da6e8] já terá adicionado o campo novo na base.
[/list:u:16d80da6e8]
A idéia de se usar transações é quando se usa os métodos [b:16d80da6e8]Append, Delete, Post e Cancel[/b:16d80da6e8] do [b:16d80da6e8]DataSet[/b:16d80da6e8] em um conjunto grande de instruções. Por exemplo:
Imagine que seu usuário inseriu 1 nota fiscal no sistema com 100 itens. O seu usuário clicou em [b:16d80da6e8]Gravar Nota[/b:16d80da6e8]. Nesse momento seu sistema deve:
[list:16d80da6e8]
[*:16d80da6e8]a. Debitar os itens do Estoque;
[*:16d80da6e8]b. Incluir um registro no Contas a Receber;
[*:16d80da6e8]c. Atualizar o fluxo de caixa com a previsão de saldo;
[*:16d80da6e8]d. Gerar automaticamente os boletos bancários;
[/list:u:16d80da6e8]
Nos processo A, B, C e D digamos que você tenha uma tabela para cada um deles. Isso significa que terá que inserir aproximandamente 1 registro em cada tabela, isso se não for gerar mais de boleto no caso de um pagamento parcelado.
Pois bem, você teria uma estrutura mais ou menos como esta no botão [b:16d80da6e8]Gravar Nota[/b:16d80da6e8]:
procedure TForm1.BotaaoGravarClick(Sender: TOject); begin with DataModule do begin -> Localiza o Produto no Estoque; -> Altera, decrementa um da quantidade, grava, ApplyUpdates(0); -> Append, Post e ApplyUpdates(0) no ContasAReber; -> Append, Post e ApplyUpdates(0); no FluxoCaixa; -> Rotina Inclusão de Boletos -> -> for I := 0 to NumeroBoletos do begin Append; -> Dados do Boleto; Post; ApplyUpdates(0); end; -> Grava Item da Nota fiscal. ApplyUpdates(0); -> Grava Nota Fiscal. ApplyUpdates(0); end; end;
Agora imagina que aconteça algum problema no meio de uma destas operações?
Você deve garantir que todos os registros de todas as tabelas sejam aplicados ou descartados. É ai que entra a Transação. Essa rotina ficaria mais ou menos assim:
procedure TForm1.BotaaoGravarClick(Sender: TOject); var TD : TTransactionDesc; begin Try TD.IsolationLevel := xilREADCOMMITTED; SQLConnection1.StartTransaction(TD); with DataModule do begin -> Localiza o Produto no Estoque; -> Altera, decrementa um da quantidade, grava, ApplyUpdates(0); -> Append, Post e ApplyUpdates(0) no ContasAReber; -> Append, Post e ApplyUpdates(0); no FluxoCaixa; -> Rotina Inclusão de Boletos -> -> for I := 0 to NumeroBoletos do begin Append; -> Dados do Boleto; Post; ApplyUpdates(0); end; -> Grava Item da Nota fiscal. ApplyUpdates(0); -> Grava Nota Fiscal. ApplyUpdates(0); end; SQLConnection1.Commit(TD); except on E:Exception do begin SQLConnection1.RollBack(TD); raise; end; end; end;
Agora sim sua operação estará salva e faz algum sentido. Caso ocorra qualquer erro durante o processamento, todos os registros envolvidos na transação são apagados e/ou alterados para voltar ao estado normal.
Fiz um exemplinho básico incluindo 4 botões na tela. Add (Alter table), Update (Altera valores na tabela), Commit e RollBack. No onCreate do form abra a transacao e faça alguns testes.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DBXpress, DB, SqlExpr, StdCtrls;
type
TForm1 = class(TForm)
SQLConnection1: TSQLConnection;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
TD: TTransactionDesc;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
TD.TransactionID := 1;
TD.IsolationLevel := xilREADCOMMITTED;
SQLConnection1.StartTransaction(TD);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SQLConnection1.ExecuteDirect(´ALTER TABLE TABELA1 ADD ORIGEM INTEGER´);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
SQLConnection1.Commit(TD);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
SQLConnection1.Rollback(TD);
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
SQLConnection1.ExecuteDirect(´UPDATE TABELA1 SET ORIGEM=1´);
end;
end.
Gostei + 0
26/09/2007
Tchelllo
resposta muito bem detalhada!!
Abraços!
Gostei + 0
26/09/2007
Fabiano Góes
cara parabéns pela explicação muito util para futuras dúvidas.
abraço !!!
Gostei + 0
27/09/2007
Adriano Santos
cara parabéns pela explicação muito util para futuras dúvidas.
abraço !!![/quote:29ab17b733]
Quê isso gente, só sinto falta do cara que mais me ensinou DBExpress direta e indiretamente: [b:29ab17b733]Vinicius2K[/b:29ab17b733]. Alguém sabe dele? Sumiu, o cara manja muito.
Vlw, abs.
Gostei + 0
27/09/2007
Fabiano Góes
cara parabéns pela explicação muito util para futuras dúvidas.
abraço !!![/quote:72808da3d0]
Quê isso gente, só sinto falta do cara que mais me ensinou DBExpress direta e indiretamente: [b:72808da3d0]Vinicius2K[/b:72808da3d0]. Alguém sabe dele? Sumiu, o cara manja muito.
Vlw, abs.[/quote:72808da3d0]
realmente o Vinicius2K manja mesmo, eu também tenho bastante dicas do Vinicius sobre DBExpress aprendi muito com ele.
abraço pra ele também !!!
Gostei + 0
Clique aqui para fazer login e interagir na Comunidade :)