Transação
Uma transação, em qualquer sistema, consiste em uma sequência de ações que são consideradas “atômicas”, ou seja, indivisíveis no conceito de trabalho.
Toda transação deve seguir o conceito de ACID (Atomicity, Consistency, Isolation e Durability - Atomicidade, Consistência, Isolamento e Durabilidade). Em termos gerais:
Atomicidade: A execução da transação é atômica. Ou seja, todas as ações são executadas ou nenhuma;
Consistência: Cada execução deve conservar a consistência do banco de dados;
Isolamento: Cada transação deve ser isolada dos efeitos de execução concorrentes de outras transações e;
Durabilidade: Toda transação que for finalizada de forma bem sucedida deve persistir os seus resultados no sistema.
Transações no Protheus
No Protheus, o controle de transações é iniciado através do comando BEGIN TRANSACTION e finalizado através do comando END TRANSACTION.
Para garantir a atomicidade da transação, dentro de uma sequencia iniciada por BEGIN TRANSACTION, todos os dados são gravados ou nenhum.
Exemplo:
Function Grava01()
BEGIN TRANSACTION
//-------------------------------------------------------------------
//Bloco de Gravação A
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
Grava02()
END TRANSACTION
Return
Function Grava02()
BEGIN TRANSACTION
//-------------------------------------------------------------------
//Bloco de Gravação B
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
END TRANSACTION
Return
No exemplo acima, caso exista um erro na rotina Grava02() ou até mesmo na Grava01(), nenhum dado será inserido no banco de dados do sistema.
A Função DisarmTransaction
A função DisarmTransaction() pode ser utilizada para forçar o RollBack dos dados já inseridos e também evitar a gravação dos dados posteriores, protegidos desde a primeira chamada do comando BEGIN TRANSACTION. Esta função deixa o sistema no estado de TTSBREAK e, a partir deste ponto, todas as transações são desfeitas, até o primeiro nível de transação aberto.
No exemplo a seguir:
Function Grava01()
BEGIN TRANSACTION
//-------------------------------------------------------------------
//Bloco de Gravação A
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
Grava02()
//-------------------------------------------------------------------
//Bloco de Gravação D
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
END TRANSACTION
Return
Function Grava02()
BEGIN TRANSACTION
//-------------------------------------------------------------------
//Bloco de Gravação B
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
DisarmTransaction()
Break
//-------------------------------------------------------------------
//Bloco de Gravação C
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
END TRANSACTION
Return
No exemplo acima, a função Grava02 executou um DisarmTransaction após a sua gravação dos dados. Neste caso, os blocos “BLOCO A”, “BLOCO B” e “BLOCO D” não serão gravados no banco de dados, e o bloco “BLOCO C” não será executado, pois, conforme veremos abaixo, após um DisarmTransaction só existem duas alternativas: Um Break, para que o fluxo seja desviado para depois do próximo comando END TRANSACTION ou a finalização da thread.
Caso uma transação seja iniciada com o sistema em TTSBREAK, o mesmo é abortado.
Function Grava01()
BEGIN TRANSACTION
//-------------------------------------------------------------------
//Bloco de Gravação A
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
Grava02()
BEGIN TRANSACTION
//-------------------------------------------------------------------
//Bloco de Gravação E
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
END TRANSACTION
END TRANSACTION
Return
Function Grava02()
BEGIN TRANSACTION
//-------------------------------------------------------------------
//Bloco de Gravação B
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
DisarmTransaction()
Break
//-------------------------------------------------------------------
//Bloco de Gravação C
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
END TRANSACTION
Grava03()
Return
Function Grava03()
BEGIN TRANSACTION
//-------------------------------------------------------------------
//Bloco de Gravação D
//Dentro deste bloco será realizado alguma gravação no banco de dados
//-------------------------------------------------------------------
END TRANSACTION
Return
No exemplo acima, após sair da função Grava02() o sistema está em TTSBREAK, e ao executar o comando BEGIN TRANSACTION dentro da função Grava03() o sistema será abortado por erro.
Controle de transações: como utilizar
A abertura de uma transação no Protheus deve ser realizada no momento de gravação dos dados, quando necessário.
A chamada da função DisarmTransaction() deve sempre ser seguida da finalização da thread ou, quando necessário, de um BREAK, para que o fluxo da rotina seja direcionado para depois do END TRANSACTION . Esta função deve ser utilizada em casos específicos, não devendo ser utilizada de maneira indiscriminada no sistema, pois pode gerar erros de gravação ou interpretação errônea do fluxo do sistema.
Caso seja necessário é possível verificar, antes de abrir uma transação, se o sistema não está em TTSBREAK.
Para saber mais
BEGIN TRANSACTION
END TRANSACTION
Guia de boas Práticas - Transações