Ruby on Rails
ActiveRecord-Transaktionen
Suche…
Bemerkungen
Transaktionen sind Schutzblöcke, bei denen SQL-Anweisungen nur dauerhaft sind, wenn sie alle als eine einzige Aktion erfolgreich sein können. Das klassische Beispiel ist eine Überweisung zwischen zwei Konten, bei denen Sie nur dann eine Einzahlung vornehmen können, wenn die Auszahlung erfolgreich war, und umgekehrt. Transaktionen erzwingen die Integrität der Datenbank und schützen die Daten vor Programmfehlern oder Datenbankausfällen. Sie sollten also grundsätzlich Transaktionsblöcke verwenden, wenn Sie mehrere Anweisungen haben, die zusammen oder nicht ausgeführt werden müssen.
Grundlegendes Beispiel
Zum Beispiel:
ActiveRecord::Base.transaction do
david.withdrawal(100)
mary.deposit(100)
end
Dieses Beispiel nimmt nur Geld von David und gibt es an Mary, wenn weder Abhebung noch Einzahlung eine Ausnahme darstellen. Ausnahmen erzwingen einen ROLLBACK, der die Datenbank vor dem Beginn der Transaktion in den Status zurücksetzt. Beachten Sie jedoch, dass die Instanzdaten der Objekte nicht in den Zustand vor der Transaktion zurückversetzt werden.
Verschiedene ActiveRecord-Klassen in einer einzelnen Transaktion
Obwohl die Transaktionsklassenmethode für einige ActiveRecord-Klasse aufgerufen wird, müssen die Objekte im Transaktionsblock nicht alle Instanzen dieser Klasse sein. Dies liegt daran, dass Transaktionen pro Datenbankverbindung und nicht pro Modell erfolgen.
In diesem Beispiel wird ein Saldasatz transaktional gespeichert, obwohl die Transaktion für die Konto-Klasse aufgerufen wird:
Account.transaction do
balance.save!
account.save!
end
Die Transaktionsmethode ist auch als Modellinstanzmethode verfügbar. Sie können beispielsweise auch Folgendes tun:
balance.transaction do
balance.save!
account.save!
end
Mehrere Datenbankverbindungen
Eine Transaktion wirkt auf eine einzelne Datenbankverbindung. Wenn Sie über mehrere klassenspezifische Datenbanken verfügen, schützt die Transaktion die Interaktion zwischen ihnen nicht. Eine Problemumgehung besteht darin, eine Transaktion für jede Klasse zu beginnen, deren Modelle Sie ändern:
Student.transaction do
Course.transaction do
course.enroll(student)
student.units += course.units
end
end
Dies ist eine schlechte Lösung, aber vollständig verteilte Transaktionen liegen nicht im Bereich von ActiveRecord.
Speichern und Zerstören werden automatisch in eine Transaktion eingeschlossen
Sowohl #save als auch #destroy werden in einer Transaktion verpackt, die sicherstellt, dass das, was Sie bei Validierungen oder Rückrufen tun, unter ihrer geschützten Abdeckung geschieht. Sie können Validierungen verwenden, um nach Werten zu after_*
, von denen die Transaktion abhängig ist, oder Sie können Ausnahmen in den Rückrufen für das Rollback after_*
, einschließlich after_*
.
Folglich werden Änderungen an der Datenbank nicht außerhalb Ihrer Verbindung sichtbar, bis der Vorgang abgeschlossen ist. Wenn Sie beispielsweise versuchen, den Index einer Suchmaschine in after_save
aktualisieren, wird der aktualisierte Datensatz nicht after_save
. Der after_commit
Callback ist der einzige, der ausgelöst wird, wenn die Aktualisierung festgeschrieben ist.
Rückrufe
Es gibt zwei Arten von Rückrufen, die mit dem after_commit
und after_rollback
Transaktionen verbunden sind: after_commit
und after_rollback
.
after_commit
Rückrufe werden für jeden Datensatz, der innerhalb einer Transaktion gespeichert oder zerstört wurde, unmittelbar nach dem after_commit
der Transaktion aufgerufen. after_rollback
Rückrufe werden für jeden Datensatz, der innerhalb einer Transaktion gespeichert oder zerstört wurde, unmittelbar nach dem after_rollback
der Transaktion oder des Sicherungspunkts aufgerufen.
Diese Callbacks sind hilfreich für die Interaktion mit anderen Systemen, da Ihnen garantiert wird, dass der Callback nur ausgeführt wird, wenn sich die Datenbank in einem permanenten Zustand befindet. after_commit
ist beispielsweise ein guter Ort, um einen Cache zu after_commit
da das Löschen innerhalb einer Transaktion dazu führen könnte, dass der Cache vor der Aktualisierung der Datenbank erneut generiert wird.
Transaktion rückgängig machen
ActiveRecord::Base.transaction
verwendet die Ausnahme ActiveRecord::Base.transaction
ActiveRecord::Rollback
, um ein absichtliches Rollback von anderen Ausnahmesituationen zu unterscheiden. Normalerweise führt das .transaction
einer Ausnahme dazu, dass die .transaction
Methode die Datenbanktransaktion .transaction
und die Ausnahme .transaction
. Wenn Sie jedoch eine ActiveRecord::Rollback
Ausnahme ActiveRecord::Rollback
, wird die Datenbanktransaktion zurückgesetzt, ohne die Ausnahme zu übergeben.
Zum Beispiel könnten Sie dies in Ihrem Controller tun, um eine Transaktion rückgängig zu machen:
class BooksController < ActionController::Base
def create
Book.transaction do
book = Book.new(params[:book])
book.save!
if today_is_friday?
# The system must fail on Friday so that our support department
# won't be out of job. We silently rollback this transaction
# without telling the user.
raise ActiveRecord::Rollback, "Call tech support!"
end
end
# ActiveRecord::Rollback is the only exception that won't be passed on
# by ActiveRecord::Base.transaction, so this line will still be reached
# even on Friday.
redirect_to root_url
end
end