Monday, March 4, 2013

Experimenting Built-in Transactions

One important feature of a reliable database system is atomicity, i.e. either a transaction succeeds as a whole or none of its statement is executed. Put it in another way, if one of its statement fails, all of its statements must not be executed. Rails has a nice clean implementation of transaction, just wrap all the rails code which you want to put within a transaction with the transaction call,

  A_CLASS.transaction do
    # ...
  end

All the code within the block that will touch the database is guaranteed to be executed on a whole or none of them are executed. However, one thing worth noting is that only the database is guaranteed on atomicity, those models involved are changed anyway. If you want to restore the models involved in the transaction if any of the database statement fails, you need to add them as parameters to the transaction method.

However, what I'm going to mess around with today is a special kind of transaction called built-in transaction. Built-in transaction guarantees the atomicity of the transaction that occurs between parent and child tables. For example, when saving a parent record, built-in transaction makes sure that either the parent record and all its related child records are saved to database, or nothing is saved to database.

Let's create two very simple classes, Order and OrderLineItem, and define a one-to-many relationship between them.
rails generate model Order total:decimal description:text
rails generate model OrderLineItem subtotal:decimal description:text order:references

class Order < ActiveRecord::Base
  attr_accessible :description, :total
  has_many :order_line_items
end

We also require that each line item's subtotal must be greater than 0.
class OrderLineItem < ActiveRecord::Base
  belongs_to :order
  attr_accessible :description, :subtotal

  # validations
  validates_numericality_of :subtotal, 
        :greater_than => 0
end

Now let's launch the console. Let's create an order , and an order line item with subtotal to be -1, and then we assign the line item to the order object. We know the line item is invalid. We also know if we save the order object, the line item will be saved too. So let's call the save method on the order object and see what happens. As expected, it throws an error saying line item subtotal must be greater than zero. Now let's check the database, we can see no order or order line item records is added to database. Then let's change subtotal of the line item object to 1, and then save the order again, now both the order and the line item are saved to database.


1 comment: