Perché sono necessarie le transazioni

Molto spesso, quando si lavora con un database, si verifica una situazione in cui è necessario eseguire molte azioni diverse, ma hanno senso solo insieme.

Ad esempio, stiamo scrivendo un software bancario che dovrebbe fare tre cose:

  • Prelevare denaro dal conto del cliente
  • Aggiungi denaro al conto del destinatario
  • Registrare i dati di registrazione nel "registro di registrazione"

Se si verifica un errore durante l'esecuzione di una di queste azioni, anche le altre due devono essere annullate. È impossibile cancellare denaro dal cliente e non aggiungerlo al destinatario? Bene, o aggiungere al destinatario, ma non cancellare dal cliente?

Quindi, un tale raggruppamento logico di diverse azioni in una sola è chiamato transazione . In altre parole, una transazione è un insieme di azioni che devono essere eseguite solo tutte insieme . Se un'azione non è riuscita o è stata eseguita con un errore, tutte le altre azioni devono essere annullate.

Una transazione di solito ha tre stati:

  • stato iniziale - lo stato del sistema prima dell'esecuzione di un gruppo di azioni
  • stato di successo - stato dopo il completamento del gruppo di azioni
  • stato fallito: qualcosa è andato storto

In questo caso, di solito ci sono tre comandi:

  • inizio/inizio - eseguito prima dell'inizio del gruppo logico di azioni
  • commit - eseguito dopo il gruppo di azioni della transazione
  • rollback : avvia il processo di ritorno del sistema dallo stato di errore allo stato iniziale

Funziona così.

Per prima cosa devi aprire una transazione: chiama il metodo begin() o start() . Chiamare questo metodo indica lo stato del sistema a cui cercheremo di tornare se qualcosa va storto.

Quindi vengono eseguite tutte le azioni, che vengono combinate in un gruppo logico: una transazione.

Quindi viene chiamato il metodo commit() . La sua chiamata segna la fine di un gruppo logico di azioni e di solito avvia anche il processo di messa in pratica di queste azioni.

Ricorda come abbiamo scritto qualcosa in FileWriter: prima tutto ciò che abbiamo scritto viene archiviato in memoria, quindi quando viene chiamato il metodo flush () , tutti i dati dal buffer in memoria vengono scritti su disco. Questo flush() è il commit della transazione.

Bene, se si è verificato un errore durante l'operazione della transazione, è necessario avviare il processo di ritorno allo stato iniziale. Questo processo è chiamato rollback() e il metodo con lo stesso nome ne è solitamente responsabile.

In parole povere, ci sono 2 modi per completare una transazione:

  • COMMIT - confermiamo tutte le modifiche apportate
  • ROLLBACK - ripristina tutte le modifiche apportate

Transazioni in JDBC

Quasi tutti i DBMS possono lavorare con le transazioni. Quindi JDBC ha anche il supporto per questo caso. Tutto è implementato in modo molto semplice.

Innanzitutto, ogni chiamata al metodo execute() dell'oggetto Statement viene eseguita in una transazione separata. Per fare ciò, Connection ha un parametro AutoCommit . Se è impostato su true , allora commit() verrà chiamato dopo ogni chiamata al metodo execute() .

In secondo luogo, se vuoi eseguire più comandi in una transazione, puoi farlo in questo modo:

  • disabilitare AutoCommit
  • chiamando i nostri team
  • chiama esplicitamente il metodo commit()

Sembra molto semplice:

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 si verifica un errore sul server mentre il metodo commit() è in esecuzione , il server SQL annullerà tutte e tre le azioni.

Ma ci sono situazioni in cui l'errore si verifica ancora sul lato client e non siamo mai arrivati ​​alla chiamata al metodo 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 si verifica un errore durante l'esecuzione di un executeUpdate() , il metodo commit() non verrà chiamato. Per eseguire il rollback di tutte le azioni eseguite, è necessario chiamare il metodo rollback() . Di solito ha questo aspetto:

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();
}

Punti di salvataggio

Con l'avvento di JDBC 3.0, è diventato possibile lavorare in modo più efficiente con il rollback delle transazioni. Ora puoi impostare punti di salvataggio - punti di salvataggio e quando chiami l' operazione rollback () , torna a un punto di salvataggio specifico.

Per salvare è necessario creare un punto di salvataggio, questo viene fatto con il comando:

Savepoint save = connection.setSavepoint();

Il ripristino di un punto di salvataggio viene eseguito con il comando:

connection.rollback(save);

Proviamo ad aggiungere un punto di salvataggio prima del nostro comando problematico:

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();
}

Abbiamo organizzato le transazioni nidificate aggiungendo un punto di salvataggio prima di chiamare il metodo problematico e tornando allo stato salvato chiamando il metodo rollback(save) .

Sì, è molto simile al salvataggio/caricamento nei giochi.