Por que as transações são necessárias

Muitas vezes, ao trabalhar com um banco de dados, surge uma situação em que você precisa realizar muitas ações diferentes, mas elas só fazem sentido juntas.

Por exemplo, estamos escrevendo um software bancário que deve fazer três coisas:

  • Retirar dinheiro da conta do cliente
  • Adicionar dinheiro à conta do destinatário
  • Registre os dados de postagem no “log de postagem”

Se ocorrer um erro durante a execução de qualquer uma dessas ações, as outras duas também devem ser canceladas. É impossível dar baixa no dinheiro do cliente e não adicioná-lo ao destinatário? Bem, ou adicionar ao destinatário, mas não cancelar do cliente?

Portanto, esse agrupamento lógico de diferentes ações em uma é chamado de transação . Em outras palavras, uma transação é um grupo de ações que devem ser executadas apenas todas juntas . Se alguma ação falhou ou foi executada com erro, todas as outras ações devem ser canceladas.

Uma transação geralmente tem três estados:

  • estado inicial - o estado do sistema antes de executar um grupo de ações
  • estado de sucesso - estado após a conclusão do grupo de ação
  • estado de falha - algo deu errado

Nesse caso, geralmente existem três comandos:

  • begin/start - executado antes do início do grupo lógico de ações
  • commit - executado após o grupo de ação da transação
  • reversão - inicia o processo de retorno do sistema do estado de falha para o estado inicial

Funciona assim.

Primeiro você precisa abrir uma transação - chame o método begin() ou start() . Chamar este método indica o estado do sistema ao qual tentaremos retornar se algo der errado.

Em seguida, todas as ações são executadas, que são combinadas em um grupo lógico - uma transação.

Em seguida, o método commit() é chamado . Sua chamada marca o fim de um conjunto lógico de ações, e também costuma iniciar o processo de colocar essas ações em prática.

Lembre-se de como escrevemos algo no FileWriter: primeiro, tudo o que escrevemos é armazenado na memória e, quando o método flush () é chamado , todos os dados do buffer na memória são gravados no disco. Este flush() é o commit da transação.

Bem, se ocorreu um erro durante a operação da transação, você precisa iniciar o processo de retorno ao estado inicial. Esse processo é chamado de rollback() , e o método de mesmo nome geralmente é responsável por ele.

Grosso modo, existem 2 maneiras de concluir uma transação:

  • COMMIT - confirmamos todas as alterações feitas
  • ROLLBACK - reverte todas as alterações feitas

Transações em JDBC

Quase todos os DBMS podem trabalhar com transações. Então JDBC também tem suporte para este caso. Tudo é implementado de forma muito simples.

Primeiro, cada chamada para o método execute() do objeto Statement é executada em uma transação separada. Para fazer isso, o Connection possui um parâmetro AutoCommit . Se for definido como true , então commit() será chamado após cada chamada para o método execute() .

Em segundo lugar, se você deseja executar vários comandos em uma transação, pode fazê-lo assim:

  • desativar AutoCommit
  • chamando nossas equipes
  • chame o método commit() explicitamente

Parece muito simples:

connection.setAutoCommit(false);

Statement statement = connection.createStatement();
int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount3 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");

connection.commit();

Se ocorrer um erro no servidor enquanto o método commit() estiver em execução , o servidor SQL cancelará todas as três ações.

Mas há situações em que o erro ainda ocorre no lado do cliente e nunca chegamos à chamada do método commit() :

connection.setAutoCommit(false);

Statement statement = connection.createStatement();
int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");

connection.commit();

Se ocorrer um erro durante a execução de um executeUpdate() , o método commit() não será chamado. Para reverter todas as ações realizadas, você precisa chamar o método rollback() . Geralmente se parece com isso:

try{
  	connection.setAutoCommit(false);

  	Statement statement = connection.createStatement();
  	int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
  	int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
  	int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");

	  connection.commit();
 }
 catch (Exception e) {
   connection.rollback();
}

pontos de salvamento

Com o advento do JDBC 3.0, tornou-se possível trabalhar com mais eficiência com rollback de transações. Agora você pode definir pontos de salvamento - pontos de salvamento e, ao chamar a operação rollback () , reverta para um ponto de salvamento específico.

Para salvar, você precisa criar um ponto de salvamento, isso é feito com o comando:

Savepoint save = connection.setSavepoint();

A reversão para um ponto de salvamento é feita com o comando:

connection.rollback(save);

Vamos tentar adicionar um ponto de salvamento antes do nosso comando problemático:

try{
  	connection.setAutoCommit(false);

  	Statement statement = connection.createStatement();
  	int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
  	int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");

  	Savepoint save = connection.setSavepoint();
 	 try{
      	int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");
 	 }
 	 catch (Exception e) {
    	   connection.rollback(save);
 	 }

	  connection.commit();
 }
 catch (Exception e) {
   connection.rollback();
}

Organizamos as transações aninhadas adicionando um ponto de salvamento antes de chamar o método problemático e retornando ao estado salvo chamando o método rollback(save) .

Sim, é muito parecido com salvar/carregar em jogos.