Google


   


You are here: CodeIdol.com > Other > Ruby Cookbook > Databases And Persistence > Using Transactions In ActiveRecord

SAVE
Digg
Shown on del.icio.us del.icio.us
See Whos Talking About This on Technorati Technorati
I've Reddit reddit

Recipe 13.16. 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 you'll 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.

Here's 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 there's 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't leave the database in a state where the author has been created but the blog entry hasn't:

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

	# The method succeeded; Carol's in the database:
	User.find(:first, :conditions=>"name='Carol'")
	# => #<User:0xb7888ae4 @attributes={"name"=>"Carol", … }>
	
	# Let's 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; David's 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.

ActiveRecord's transactions piggyback on top of database transactions, so they'll only work if your database supports transactions. Most databases do these days; chances are you won't have trouble unless you're using a MySQL database and not using InnoDB tables. However, most of the open source databases don't support nested transactions, so you're 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're 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 that's consulted by the application, but not stored in the database.

See Also


SAVE
Digg
Shown on del.icio.us del.icio.us
See Whos Talking About This on Technorati Technorati
I've Reddit reddit

You are here: CodeIdol.com > Other > Ruby Cookbook > Databases And Persistence > Using Transactions In ActiveRecord


ADBRITE ads links
   
Related tags







Popular Categories
Unix books and guides

AJAX popular information
C# language guides
Windows books and cookbooks

.......








Business Key Top Sites

be number one
rate your site




    С 2009 года мы стали переводить структура сайта на различные языки. Сайт теперь будет содержать книги не только на английском языке, но также и на других европейских языках, в том числе и на Русском языке.

    Русский Polski Francais Deutsch
    support sitemap terms

© CodeIdol Labs, 2007 - 2009