Using Transactions in ActiveRecord

Problem

You want to perform database operations as a group: if one of the operations fails, it should be as though none of them had ever happened.

Solution

Include active_record/ transactions, and youll give each ActiveRecord class a TRansaction method. This method starts a database transaction, runs a code block, then commits the transaction. If the code block throws an exception, the database transaction is rolled back.

Heres some simple initialization code to give ActiveRecord access to the database tables for the weblog system first seen in Recipe 13.11:

	require cookbook_dbconnect
	activerecord_connect # See chapter introduction

	class User < ActiveRecord::Base
	 has_and_belongs_to_many :blog_posts
	end

	class BlogPost < ActiveRecord::Base
	 has_and_belongs_to_many :authors, :class_name => User
	end

The create_from_new_author method below creates a new entry in the users table, then associates it with a new entry in the blog_posts table. But theres a 50% chance that an exception will be thrown right after the new author is created. If that happens, the author creation is rolled back: in effect, it never happened.

	require active_record/ 
transactions

	class BlogPost
	 def BlogPost.create_from_new_author(author_name, title, content)
	 transaction do
	 author = User.create(:name => author_name)
	 raise Random failure! if rand(2) == 0
	 create(:authors => [author], :title => title, :content => content)
	 end
	 end
	end

Since the whole operation is enclosed within a TRansaction block, an exception won leave the database in a state where the author has been created but the blog entry hasn :

	BlogPost.create_from_new_author(Carol, The End Is Near,
	 A few more facts of doom…)
	# => #

	# The method succeeded; Carols in the database:
	User.find(:first, :conditions=>"name=Carol")
	# => #"Carol", … }>
	
	# Lets do another one…
	BlogPost.create_from_new_author(David, The End: A Rebuttal,
	 The end is actually quite far away…)
	# RuntimeError: Random failure!

	# The method failed; Davids not in the database:
	User.find(:first, :conditions=>"name=David")
	# => nil

Discussion

You should use database transactions whenever one database operation puts the database into an inconsistent state, and a second operation brings the database back into consistency. All kinds of things can go wrong between the first and second operation. The database server might crash or your application might throw an exception. The Ruby interpreter might decide to stop running your thread for an arbitrarily long time, giving other threads a chance to marvel at the inconsistent state of the database. An inconsistent database can cause problems that are very difficult to debug and fix.

ActiveRecords transactions piggyback on top of database transactions, so theyll only work if your database supports transactions. Most databases do these days; chances are you won have trouble unless you e using a MySQL database and not using InnoDB tables. However, most of the open source databases don support nested transactions, so you e limited to one transaction at a time with a given database connection.

In addition to a code block, the transaction method can take a number of ActiveRecord objects. These are the objects that participate in the transaction. If the transaction fails, then not only will the database be restored to its previous state, so will the member variables of the objects.

This is useful if you e defining a method that modifies ActiveRecord objects themselves, not just the database representations of those objects. For instance, a shopping cart object might keep a running total thats consulted by the application, but not stored in the database.

See Also

  • http://wiki.rubyonrails.com/rails/pages/HowToUseTransactions
  • http://rubyonrails.org/api/classes/ActiveRecord/Transactions/ClassMethods.html


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net