Varför transaktioner behövs

Mycket ofta, när man arbetar med en databas, uppstår en situation när man behöver utföra många olika åtgärder, men de är bara meningsfulla tillsammans.

Till exempel skriver vi bankprogram som ska göra tre saker:

  • Ta ut pengar från kundens konto
  • Lägg till pengar på mottagarens konto
  • Registrera inläggsdata i "inläggsloggen"

Om ett fel uppstår under utförandet av någon av dessa åtgärder, måste de andra två också avbrytas. Det är omöjligt att skriva av pengar från kunden och inte lägga till dem till mottagaren? Tja, eller lägga till mottagaren, men inte skriva av från klienten?

Så en sådan logisk gruppering av olika åtgärder till en kallas en transaktion . Med andra ord är en transaktion en grupp av åtgärder som endast måste utföras tillsammans . Om någon åtgärd misslyckades eller utfördes med ett fel måste alla andra åtgärder avbrytas.

En transaktion har vanligtvis tre tillstånd:

  • initialtillstånd - systemets tillstånd innan en grupp av åtgärder utförs
  • framgångstillstånd - tillstånd efter att åtgärdsgruppen är klar
  • misslyckat tillstånd - något gick fel

I det här fallet finns det vanligtvis tre kommandon:

  • start/start - exekveras före starten av den logiska gruppen av åtgärder
  • commit - utförs efter transaktionsåtgärdsgruppen
  • rollback - startar processen att återställa systemet från misslyckat tillstånd till initialt tillstånd

Det fungerar så här.

Först måste du öppna en transaktion - anropa start() eller start()- metoden . Att anropa denna metod indikerar tillståndet för systemet som vi kommer att försöka återvända till om något går fel.

Sedan utförs alla åtgärder, som kombineras till en logisk grupp - en transaktion.

Sedan kallas metoden commit() . Dess uppmaning markerar slutet på en logisk grupp av åtgärder och startar också vanligtvis processen att omsätta dessa åtgärder i praktiken.

Kom ihåg hur vi skrev något i FileWriter: först lagras allt vi skrev i minnet, och sedan när flush () -metoden anropas skrivs all data från bufferten i minnet till disken. Denna flush() är transaktionsbekräftelsen.

Tja, om ett fel inträffade under transaktionens drift, måste du initiera processen för att återgå till starttillståndet. Denna process kallas rollback() , och metoden med samma namn är vanligtvis ansvarig för den.

Grovt sett finns det två sätt att slutföra en transaktion:

  • ÅTGÄRDER - vi bekräftar alla ändringar som gjorts
  • ROLLBACK - rulla tillbaka alla gjorda ändringar

Transaktioner i JDBC

Nästan alla DBMS kan arbeta med transaktioner. Så JDBC har också stöd för det här fallet. Allt genomförs väldigt enkelt.

Först exekveras varje anrop till execute() -metoden för Statement-objektet i en separat transaktion. För att göra detta har Connection en AutoCommit- parameter . Om den är satt till true kommer commit() att anropas efter varje anrop till execute()- metoden .

För det andra, om du vill utföra flera kommandon i en transaktion, kan du göra det så här:

  • inaktivera AutoCommit
  • kallar våra kommandon
  • anropa commit() -metoden explicit

Det ser väldigt enkelt ut:

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

Om ett fel uppstår på servern medan commit() -metoden körs kommer SQL-servern att avbryta alla tre åtgärderna.

Men det finns situationer när felet fortfarande uppstår på klientsidan, och vi kom aldrig till commit()- metodanropet :

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

Om ett fel inträffar under körningen av en executeUpdate() , kommer metoden commit() inte att anropas. För att återställa alla vidtagna åtgärder måste du anropa metoden rollback() . Det brukar se ut så här:

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

Räddningspunkter

Med tillkomsten av JDBC 3.0 blev det möjligt att arbeta mer effektivt med återställning av transaktioner. Nu kan du ställa in spara poäng - spara poäng, och när du anropar återställningsoperationen () rullar du tillbaka till en specifik sparpunkt.

För att spara behöver du skapa en räddningspunkt, detta görs med kommandot:

Savepoint save = connection.setSavepoint();

Återgå till en räddningspunkt görs med kommandot:

connection.rollback(save);

Låt oss försöka lägga till en räddningspunkt före vårt problematiska kommando:

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

Vi organiserade kapslade transaktioner genom att lägga till en spara-punkt innan vi anropade den problematiska metoden och återvände till det sparade tillståndet genom att anropa rollback(save) -metoden .

Ja, det är väldigt likt att spara/ladda i spel.