Como trabalhar com multiplas transações utilizando dbExpress Framework
Olá pessoal, mais um post sobre dbExpress Framework, desta vez vamos focar em controle de transações. É comum termos situações onde precisamos ter mais de uma transação em uma mesma conexão ao banco de dados, essa tarefa no dbExpress é bem simples.
O método TDBXConnection.BeginTransaction inicia uma transação no banco de dados, o tipo desta transação irá depender do parâmetro a ser passado durante a criação da transação, estes tipos podem ser:
- TDBXIsolations.ReadCommitted
- TDBXIsolations.RepeatableRead
- TDBXIsolations.DirtyRead
- TDBXIsolations.Serializable
- TDBXIsolations.SnapShot
Você pode consultar a documentação do RAD Studio online e entender cada tipo de isolamente transacional aqui. Importante lembrar que o isolamento transacional irá variar de banco para banco, verifica quais o seu banco de dados suporta.
Ao iniciar uma transação o método BeginTransaction retornará um objeto do tipo TDBXTransaction onde você poderá efetuar o Commit e Rollback da transação.
No exemplo abaixo temos duas transações onde através de uma efetuamos 5 inserts, o resultado da query mostra que os registros estão no banco de dados e não comitados ainda, a partir dai iniciamos outra transação com o nível de isolamento ReadCommited, que quer dizer trazer todos os registrados que estejam efetivamente “comitados” no banco, como ainda não fizemos o commit, o resultados do select virá fazio, ao final efetuamos o Rollback para que os registros não sejam efetivamente gravados na base de dados e depois efetuamos um commit na transação de consulta.
O código ficaria assim:
program MultipleTransaction; {$APPTYPE CONSOLE} uses SysUtils, DBXDynalink, DBXCommon, DBXInterbase; Const InsertSQL : String = 'Insert Into Country Values ( ''%s'', ''%s'' )'; var ConnName: string; Conn: TDBXConnection; TransSelect, TransInsert : TDBXTransaction; Cmd: TDBXCommand; Reader: TDBXReader; i : integer; begin Conn := TDBXConnectionFactory.GetConnectionFactory.GetConnection ('employee ib', 'sysdba', 'masterkey'); if Conn <> nil then begin Cmd := Conn.CreateCommand; // Start transaction - only inserts TransInsert := Conn.BeginTransaction(TDBXIsolations.ReadCommitted); for I := 1 to 5 do begin Cmd.Text := Format(InsertSQL, [ 'Record ' + IntToStr(I), IntToStr(I)]); Cmd.Prepare; Cmd.ExecuteUpdate; end; // Prepare and execute the SQL Statement Cmd.Text := 'SELECT * FROM Country Where Country Like ''Rec%'''; Cmd.Prepare; Reader := Cmd.ExecuteQuery; Writeln('========== First Select including the new 5 records'); while Reader.Next do Writeln(Reader.Value['Country'].GetAnsiString); // Start new transaction - doesn't include the inserted record TransSelect := Conn.BeginTransaction(TDBXIsolations.DirtyRead); Writeln('===================================================='); Writeln(''); Reader := Cmd.ExecuteQuery; Writeln('========== Second Select based on new transaction'); while Reader.Next do Writeln(Reader.Value['Country'].GetAnsiString); // Commit Select transaction and Rollback the Insert transaction Conn.CommitFreeAndNil(TransSelect); Conn.RollbackFreeAndNil(TransInsert); Readln; Reader.Free; Cmd.Free; Conn.Free; end; end.
Ao executar o código acima, temos o seguinte resultado:
Prezado Sr. Adreano
Bom dia
O sr. conhece alguma ferramenta similar ao Genexus ou Maker que possa gerar o código do sistema modelado para Delphi?
Ou seja uma ferramenta CASE para Delphi?
já ví o x-maker mas achei desatualizado e não trabalha com a versão atual do delphi.
grato,
Prof. Emiliano Monteiro
Emiliano, se você procurar no Google “ferramenta case para delphi” encontrará várias opções. Eu não poderia recomendar alguma em específico, pois não sei o que você espera de uma ferramenta case.
Eu particularmente não gosto de ferramentas cases, as que você citou geram uma falsa impressão de produtividade, pois depois a manutenção se torna difícil e custosa.
O Delphi por si só oferece inúmeros recursos que permitem o desenvolvedor criar aplicações de alta qualidade com alta produtividade.
Andreano, muito obrigado por mais este artigo, algum tempo atraz estava preocupado pois não havia conseguido trabalhar isso no DBX4 e não encontrava a documentação.
Beleza Andreano!
Seguinte, na linha 53, não deveria continuar o nível de isolamento ReadCommited?
E sobre a intercalação de transações, como faço para começar duas transações, e escolher através de qual transação quero rodar um comando? Ouitro exemplo, em uma aplicação DataSnap, onde o objeto de conexão é compartilhado, como faço para o BeginTransaction de um cliente não interferir nos demais clientes?
Ainda sobre o DataSnap, verifiquei que posso verificar qual usuário está rodando um comando verificando o ThreadID, este procedimento é seguro?
Abraço,
Tatu!
Oi Carlos,
Basta ter objetos transacionais diferentes. Em uma aplicação DataSnap você irá controlar transação no server method ou através de ClientDataSet, não tem conflito.
Sobre o ThreadID sim, tudo no Datasnap é thread safe
Sobre meu post anterior: quanta utilização do verbo verificar hehehe… não ficou bom, mas acho que deu para entender.
Carlos, não entendi nada 🙂
Sr. Andreano,
Multiplas transações em oracle utilizando Delphi 2007 eu não consigo fazer como você fez no seu artigo. Como faço nesse cenário ?
Obrigado!
@Rodrigo, o suporte a multiplas transações foi implementado a partir do Delphi XE, o 2007 não suporta mesmo não.
Bom dia a todos! Andreano poderia responder uma dúvida? Se tenho várias classes DAO que são responsáveis por consultas no banco, como poderia fazer da melhor forma para usar a DBXExpress Framework, teria que ter em todo método uma conexão, por ex.:
*****método de pesquisa*****
aDBXConn:= TDBXConnectionFactory.GetConnectionFactory.GetConnection
(‘nomedaconexaodbxexpress’, ‘user’, ‘password’);
aCmnd := aDBXConn.CreateCommand;
aDBXTrans := aDBXConn.BeginTransaction(TDBXIsolations.ReadCommitted);
// Start transaction
aCmnd.Text := ‘sql’;
aCmnd.Prepare;
aReader := aCmnd.ExecuteQuery;
…. continua….
*****método para segunda pesquisa*****
aDBXConn:= TDBXConnectionFactory.GetConnectionFactory.GetConnection
(‘nomedaconexaodbxexpress’, ‘user’, ‘password’);
…… continua
ou teria outra forma melhor de implementar? Tipo criando uma classe de conexão, etc. Como sugestão poderia nos mostrar em um outro post como usar o DBXExpress Framework para Criar aplicações MVC.
Grato e até mais…
@Clécio, recomendo você criar uma classe que tenha uma método que retorne a conexão quando você precisar.
boa tarde,
excelente post, mas como ficaria a formatacao para um campo date inves de uma string
InsertSQL : String = ‘Insert Into Country Values ( ”%s”, ”%s” )’;
Ozamir,
só passar a data como string, você pode usar DateToStr para converter
Andreano,
estou realizando a mesma Rotina que voce demonstrou, porém no meu caso aparesenta erro na linha 53;
” A transaction is already active.”
Estou utilizando o Delphi 2010 – MYSQL 51.1.53 (INNODB)
O que poderia ser ?
Att,
Após varias consultas na documentação do Mysql:
http://dev.mysql.com/doc/refman/5.1/en/dynindex-isolevel.html
“Eu” não encontrei uma solução prática para meu problema.
Ou seja, não tem como voce criar mais que uma transação na mesma sessão, para resolver o problema que eu tinha postado no tópico anterior, tive que criar uma nova sessão(Conexão) para cada transação que eu tinha.
Na minha aplicação o usuário pode abrir abrir várias abas(TabSheet) – TDI e cada aba tem sua transacao independente uma da outra.
Imagine duas abas de cadastro sendo alterada ao mesmo tempo, no modelo anterior o primeiro cadastro que executasse um COMMIT os dois cadastro em aberto eram comitados.
para resolver o problema, tive que criar em cada Aba Aberta uma nova sessão no banco:
Conexao := TDBXConnectionFactory.GetConnectionFactory.GetConnection(‘Falcon Financas’, ‘falcon’, ‘**********’);
e apos fechar a aba matar a sessão ativa.
Somente assim consegui resolver o problema de varias transaçoes ser executadas ao mesmo tempo, sem nenhuma interferir com a outra.
Att, Marlon Nardi
Quanto as aplicações fireDAC, qual a forma correta de se gravar as informações no PostgreSQL e se tem algum artigo detalhando sobre esse assunto. Somente o Post() seria suficiente para gravar os dados. O FireDAC já faz todo resto automático?
Ola Boa tarde estou com erro ao iniciar a transação no SQLConnection com SQLServer. O erro é esse : First chance exception at $772E3572. Exception class TDBXError with message
‘
Foi negada uma solicitação para estabelecer conexão com o gerenciador de transações.
‘.
Alguém sabe o que pode ser ?
Obrigado.