Dlaczego potrzebne są transakcje
Bardzo często podczas pracy z bazą danych dochodzi do sytuacji, kiedy trzeba wykonać wiele różnych czynności, ale tylko razem mają one sens.
Na przykład piszemy oprogramowanie bankowe, które powinno robić trzy rzeczy:
- Wypłać pieniądze z konta klienta
- Dodaj pieniądze do konta odbiorcy
- Zapisz dane o wysłaniu w „dzienniku wysyłania”
Jeśli podczas wykonywania którejkolwiek z tych akcji wystąpi błąd, pozostałe dwie również muszą zostać anulowane. Nie da się odpisać pieniędzy od klienta i nie dodać do odbiorcy? Cóż, czy dodać do odbiorcy, ale nie odpisać od klienta?
Tak więc takie logiczne zgrupowanie różnych działań w jedną nazywa się transakcją . Innymi słowy, transakcja to grupa działań, które muszą być wykonane tylko razem . Jeśli jakakolwiek akcja nie powiodła się lub została wykonana z błędem, wszystkie inne akcje muszą zostać anulowane.
Transakcja ma zwykle trzy stany:
- stan początkowy - stan systemu przed wykonaniem grupy akcji
- stan sukcesu - stan po zakończeniu grupy akcji
- stan nieudany - coś poszło nie tak
W takim przypadku zwykle są trzy polecenia:
- begin/start - wykonywany przed rozpoczęciem logicznej grupy akcji
- zatwierdzenie - wykonywane po grupie akcji transakcji
- rollback - rozpoczyna proces przywracania systemu ze stanu awarii do stanu początkowego
To działa tak.
Najpierw musisz otworzyć transakcję - wywołaj metodę begin() lub start() . Wywołanie tej metody wskazuje stan systemu, do którego będziemy próbowali wrócić, jeśli coś pójdzie nie tak.
Następnie wykonywane są wszystkie akcje, które są łączone w logiczną grupę - transakcję.
Następnie wywoływana jest metoda commit() . Jego wezwanie oznacza koniec logicznej grupy działań, a także zwykle rozpoczyna proces wprowadzania tych działań w życie.
Przypomnij sobie, jak napisaliśmy coś w FileWriter: najpierw wszystko, co napisaliśmy, jest przechowywane w pamięci, a następnie, gdy wywoływana jest metoda flush () , wszystkie dane z bufora w pamięci są zapisywane na dysku. To flush() jest zatwierdzeniem transakcji.
Otóż jeśli podczas operacji transakcji wystąpił błąd, to należy zainicjować proces powrotu do stanu początkowego. Proces ten nazywa się rollback() i zwykle odpowiada za niego metoda o tej samej nazwie.
Z grubsza rzecz biorąc, istnieją 2 sposoby zakończenia transakcji:
- COMMIT - potwierdzamy wszystkie wprowadzone zmiany
- ROLLBACK - cofnij wszystkie wprowadzone zmiany
Transakcje w JDBC
Prawie każdy DBMS może pracować z transakcjami. Więc JDBC ma również wsparcie dla tego przypadku. Wszystko jest realizowane bardzo prosto.
Po pierwsze, każde wywołanie metody execute() obiektu Statement jest wykonywane w oddzielnej transakcji. Aby to zrobić, Connection ma parametr AutoCommit . Jeśli jest ustawiona na true , to commit() będzie wywoływana po każdym wywołaniu metody execute() .
Po drugie, jeśli chcesz wykonać kilka poleceń w jednej transakcji, możesz to zrobić w ten sposób:
- wyłącz automatyczne zatwierdzanie
- dzwoniąc do naszych zespołów
- wywołaj jawnie metodę commit().
Wygląda to bardzo prosto:
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();
Jeśli podczas działania metody commit() na serwerze wystąpi błąd , serwer SQL anuluje wszystkie trzy akcje.
Ale zdarzają się sytuacje, w których błąd nadal występuje po stronie klienta i nigdy nie dotarliśmy do wywołania metody 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();
Jeśli podczas wykonywania jednej funkcji executeUpdate() wystąpi błąd , metoda commit() nie zostanie wywołana. Aby cofnąć wszystkie podjęte działania, należy wywołać metodę rollback() . Zwykle wygląda to tak:
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();
}
Punkty zapisu
Wraz z pojawieniem się JDBC 3.0 stała się możliwa bardziej wydajna praca z wycofywaniem transakcji. Teraz możesz ustawić punkty zapisu - punkty zapisu, a kiedy wywołasz operację rollback () , cofnij się do określonego punktu zapisu.
Aby zapisać, musisz utworzyć punkt zapisu, odbywa się to za pomocą polecenia:
Savepoint save = connection.setSavepoint();
Powrót do punktu zapisu odbywa się za pomocą polecenia:
connection.rollback(save);
Spróbujmy dodać punkt zapisu przed naszym problematycznym poleceniem:
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();
}
Zorganizowaliśmy zagnieżdżone transakcje, dodając punkt zapisu przed wywołaniem problematycznej metody i powracając do zapisanego stanu, wywołując metodę rollback(save) .
Tak, jest to bardzo podobne do zapisywania/ładowania w grach.
GO TO FULL VERSION