De ce sunt necesare tranzacții

Foarte des, atunci când lucrați cu o bază de date, apare o situație în care trebuie să efectuați multe acțiuni diferite, dar acestea au sens doar împreună.

De exemplu, scriem software bancar care ar trebui să facă trei lucruri:

  • Retrage bani din contul clientului
  • Adăugați bani în contul destinatarului
  • Înregistrați datele postării în „jurnalul de postare”

Dacă apare o eroare în timpul executării oricăreia dintre aceste acțiuni, atunci și celelalte două trebuie anulate. Este imposibil să ștergi bani de la client și să nu-i adaugi la destinatar? Ei bine, sau adăugați la destinatar, dar nu anulați de la client?

Deci, o astfel de grupare logică a diferitelor acțiuni într-una singură se numește tranzacție . Cu alte cuvinte, o tranzacție este un grup de acțiuni care trebuie efectuate numai împreună . Dacă orice acțiune a eșuat sau a fost executată cu o eroare, atunci toate celelalte acțiuni trebuie să fie anulate.

O tranzacție are de obicei trei stări:

  • stare inițială - starea sistemului înainte de executarea unui grup de acțiuni
  • stare de succes - stare după ce grupul de acțiuni este finalizat
  • stare eșuată - ceva a mers prost

În acest caz, există de obicei trei comenzi:

  • start/start - executat înainte de începerea grupului logic de acțiuni
  • commit - executat după grupul de acțiuni de tranzacție
  • rollback - începe procesul de întoarcere a sistemului din starea eșuată la starea inițială

Funcționează așa.

Mai întâi trebuie să deschideți o tranzacție - apelați metoda begin() sau start() . Apelarea acestei metode indică starea sistemului la care vom încerca să revenim dacă ceva nu merge bine.

Apoi sunt efectuate toate acțiunile, care sunt combinate într-un grup logic - o tranzacție.

Apoi metoda commit() este numită . Chemarea acestuia marchează sfârșitul unui grup logic de acțiuni și, de asemenea, începe, de obicei, procesul de punere în practică a acestor acțiuni.

Amintiți-vă cum am scris ceva în FileWriter: mai întâi, tot ce am scris este stocat în memorie, iar apoi, atunci când metoda flush () este apelată , toate datele din memoria tampon sunt scrise pe disc. Acest flush() este confirmarea tranzacției.

Ei bine, dacă a apărut o eroare în timpul operațiunii tranzacției, atunci trebuie să inițiați procesul de revenire la starea de pornire. Acest proces se numește rollback() și metoda cu același nume este de obicei responsabilă pentru el.

În linii mari, există 2 moduri de a finaliza o tranzacție:

  • COMMIT - confirmăm toate modificările făcute
  • ROLLBACK - derulează înapoi toate modificările efectuate

Tranzacții în JDBC

Aproape fiecare DBMS poate lucra cu tranzacții. Deci JDBC are și suport pentru acest caz. Totul este implementat foarte simplu.

În primul rând, fiecare apel la metoda execute() a obiectului Statement este executat într-o tranzacție separată. Pentru a face acest lucru, Connection are un parametru AutoCommit . Dacă este setat la true , atunci commit() va fi apelat după fiecare apel la metoda execute() .

În al doilea rând, dacă doriți să executați mai multe comenzi într-o singură tranzacție, atunci o puteți face astfel:

  • dezactivați AutoCommit
  • apelând comenzile noastre
  • apelați metoda commit() în mod explicit

Pare foarte simplu:

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

Dacă apare o eroare pe server în timp ce metoda commit() rulează , atunci serverul SQL va anula toate cele trei acțiuni.

Dar există situații în care eroarea apare în continuare pe partea clientului și nu am ajuns niciodată la apelul metodei 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();

Dacă apare o eroare în timpul executării unui executeUpdate() , atunci metoda commit() nu va fi apelată. Pentru a anula toate acțiunile efectuate, trebuie să apelați metoda rollback() . De obicei arată așa:

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

Puncte de salvare

Odată cu apariția JDBC 3.0, a devenit posibil să se lucreze mai eficient cu derularea tranzacțiilor. Acum puteți seta puncte de salvare - puncte de salvare, iar când apelați operația de derulare () , reveniți la un anumit punct de salvare.

Pentru a salva, trebuie să creați un punct de salvare, acest lucru se face cu comanda:

Savepoint save = connection.setSavepoint();

Revenirea la un punct de salvare se face cu comanda:

connection.rollback(save);

Să încercăm să adăugăm un punct de salvare înainte de comanda noastră problematică:

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

Am organizat tranzacțiile imbricate adăugând un punct de salvare înainte de a apela metoda problematică și revenind la starea salvată apelând metoda rollback(save) .

Da, este foarte asemănător cu salvarea/încărcarea în jocuri.