| In: |
vendor/activerecord/lib/active_record/transactions.rb
|
Transactions are protective blocks where SQL statements are only permanent if they can all succed as one atomic action. The classic example is a transfer between two accounts where you can only have a deposit if the withdrawal succedded and vice versa. Transaction enforce the integrity of the database and guards the data against program errors or database break-downs. So basically you should use transaction blocks whenever you have a number of statements that must be executed together or not at all. Example:
Account.transaction do
david.withdrawal(100)
mary.deposit(100)
end
This example will only take money from David and give to Mary if neither withdrawal nor deposit raises an exception. Exceptions will force a ROLLBACK that returns the database to the state before the transaction was begun. Be aware, though, that the objects by default will not have their instance data returned to their pre-transactional state.
You can enable object-level transactions for Active Record objects, though. You do this by naming the each of the Active Records that you want to enable object-level transactions for, like this:
Account.transaction(david, mary) do
david.withdrawal(100)
mary.deposit(100)
end
If the transaction fails, David and Mary will be returned to their pre-transactional state. No money will have changed hands in neither object nor database.
Also have in mind that exceptions thrown within a transaction block will be propagated (after triggering the ROLLBACK), so you should be ready to catch those in your application code.
Tribute: Object-level transactions are implemented by Transaction::Simple by Austin Ziegler.
# File vendor/activerecord/lib/active_record/transactions.rb, line 53
53: def transaction(*objects, &block)
54: Thread.current['transaction_running'] ||= 0
55: @@mutex.lock if Thread.current['transaction_running'] == 0
56:
57: begin
58: objects.each { |o| o.extend(Transaction::Simple) }
59: objects.each { |o| o.start_transaction }
60: connection.begin_db_transaction if Thread.current['transaction_running'] == 0
61: Thread.current['transaction_running'] += 1
62:
63: block.call
64:
65: Thread.current['transaction_running'] -= 1
66: connection.commit_db_transaction if Thread.current['transaction_running'] == 0
67: objects.each { |o| o.commit_transaction }
68: rescue Exception => exception
69: Thread.current['transaction_running'] -= 1
70: connection.rollback_db_transaction if Thread.current['transaction_running'] == 0
71: objects.each { |o| o.abort_transaction }
72: raise exception
73: ensure
74: @@mutex.unlock
75: end
76: end