Ruby on Rails
ActiveRecord-transaktioner
Sök…
Anmärkningar
Transaktioner är skyddande block där SQL-uttalanden endast är permanenta om de alla kan lyckas som en atomaktivitet. Det klassiska exemplet är en överföring mellan två konton där du bara kan ha en insättning om uttagningen lyckades och vice versa. Transaktioner säkerställer databasens integritet och skyddar data mot programfel eller databasfördelningar. Så i princip bör du använda transaktionsblock när du har ett antal uttalanden som måste köras tillsammans eller inte alls.
Grundläggande exempel
Till exempel:
ActiveRecord::Base.transaction do
david.withdrawal(100)
mary.deposit(100)
end
Detta exempel tar bara pengar från David och ger dem till Mary om varken uttag eller insättning ger ett undantag. Undantag kommer att tvinga fram en ROLLBACK som returnerar databasen till staten innan transaktionen inleddes. Var dock medveten om att objekten inte kommer att få sina instansdata tillbaka till deras pre-transaktionstillstånd.
Olika ActiveRecord-klasser i en enda transaktion
Även om transaktionsklassmetoden kallas för någon ActiveRecord-klass, behöver inte objekten inom transaktionsblocket alla vara instanser av den klassen. Detta beror på att transaktioner är per databasanslutning, inte per modell.
I det här exemplet sparas en balanspost transaktionsmässigt även om transaktion anropas i kontoklassen:
Account.transaction do
balance.save!
account.save!
end
Transaktionsmetoden finns också som en modellinstansmetod. Till exempel kan du också göra detta:
balance.transaction do
balance.save!
account.save!
end
Flera databasanslutningar
En transaktion verkar på en enda databasanslutning. Om du har flera klassspecifika databaser skyddar transaktionen inte interaktionen mellan dem. En lösning är att påbörja en transaktion på varje klass vars modeller du ändrar:
Student.transaction do
Course.transaction do
course.enroll(student)
student.units += course.units
end
end
Detta är en dålig lösning, men helt distribuerade transaktioner ligger utanför ActiveRecord.
spara och förstöra är automatiskt inslagna i en transaktion
Både #save och #destroy kommer inslagna i en transaktion som säkerställer att allt du gör i valideringar eller återuppringningar kommer att ske under dess skyddade skydd. Så du kan använda valideringar för att kontrollera om värden som transaktionen beror på eller så kan du höja undantag från återuppringningar till återuppringning, inklusive after_*
återuppringningar.
Som en konsekvens ses inte ändringar i databasen utanför din anslutning förrän operationen är klar. Till exempel, om du försöker att uppdatera index för en sökmotor i after_save
indexeraren inte ser uppdaterat register. after_commit
återuppringning är den enda som utlöses när uppdateringen har genomförts.
callbacks
Det finns två typer av återuppringningar förknippade med att genomföra och rulla tillbaka transaktioner: after_commit
och after_rollback
.
after_commit
återuppringningar kallas på varje post som sparats eller förstörts inom en transaktion omedelbart efter det att transaktionen har begåtts. after_rollback
återuppringningar kallas på varje post som sparats eller förstörts inom en transaktion omedelbart efter det att transaktionen eller sparpunkten har rullats tillbaka.
Dessa återuppringningar är användbara för att interagera med andra system eftersom du garanteras att återuppringningen endast körs när databasen är i permanent tillstånd. Exempelvis är after_commit
en bra plats att sätta i en krok för att rensa en cache eftersom rensning av den inom en transaktion kan leda till att cachen regenereras innan databasen uppdateras.
Rulla tillbaka en transaktion
ActiveRecord::Base.transaction
använder ActiveRecord::Rollback
undantaget för att skilja en avsiktlig rollback från andra exceptionella situationer. Normalt kommer en höjning av ett undantag att medföra .transaction
att återuppta databastransaktionen och vidarebefordra undantaget. Men om du höjer ett ActiveRecord::Rollback
undantag rullas databastransaktionen tillbaka utan att vidarebefordra undantaget.
Till exempel kan du göra detta i din controller för att återuppta en transaktion:
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