プログラマとプロマネのあいだ

プログラマもやるし、プロマネもやるし、たまに似非アーキとか営業っぽいこともやる

「RailsによるアジャイルWebアプリケーション開発」19章まで読了

トランザクションのお話。

本書中では、一テーブルに対するトランザクション(よくある残高の問題)の例だったのですが、1トランザクション内での複数テーブルへの同時更新というのも良くある話なので、こちらをサンプルとして実装することにしました。
例として、以前扱った、注文(Order)と請求書(Invoice)のサンプルを使います。


で、トランザクションを実装する基本形は、

modelClass.transaction do
  ...
end

なのですが、複数テーブルにまたがる場合は、どちらのモデルクラスを書けば良いのかが不明だったので、両方使ってみました。
結果的には、どちらで書いても良いようです。

require 'rubygems'
require 'activerecord'

ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "../db/development.sqlite3")

class Order < ActiveRecord::Base
  has_one :invoice
end

class Invoice < ActiveRecord::Base
  belongs_to :order
  validates_presence_of :name
end

order1 = Order.new(
	:name => "taka_2",
	:address => "Kanagawa prefecture",
	:email => "taka_2@test.com",
	:payment_type_id => 1)

order2 = Order.new(
	:name => "taka_22",
	:address => "Kanagawa prefecture",
	:email => "taka_22@test.com",
	:payment_type_id => 1)

inv1 = Invoice.new(
	:order_id => order1.id,
	:name => "taka_22")

inv2 = Invoice.new(
        :order_id => order2.id,
	:name => "")

puts("Order.transactionを使う")
begin
    Order.transaction do
        order1.save!
        inv1.save!
    end
rescue
    puts 'order1 / inv1の保存に失敗しました。'
end

# 後始末
Order.delete(order1.id)
Invoice.delete(inv1.id)

puts("Invoice.transactionを使う")
begin
    Invoice.transaction do
        order1.save!
        inv1.save!
    end
rescue
    puts 'order1 / inv1の保存に失敗しました。'
end

# 後始末
Order.delete(order1.id)
Invoice.delete(inv1.id)

puts("Order.transactionを使う(Fail)")
begin
    Order.transaction do
        order2.save!
        inv2.save!
    end
rescue
    puts 'order2 / inv2の保存に失敗しました。'
end

# 後始末
#Order.delete(order2.id)
#Invoice.delete(inv2.id)

puts("Invoice.transactionを使う(Fail)")
begin
    Invoice.transaction do
        order2.save!
        inv2.save!
    end
rescue
    puts 'order2 / inv2の保存に失敗しました。'
end

# 後始末
#Order.delete(order2.id)
#Invoice.delete(inv2.id)

結果はこう。

Order.transactionを使う
Invoice.transactionを使う
Order.transactionを使う(Fail)
order2 / inv2の保存に失敗しました。
Invoice.transactionを使う(Fail)
order2 / inv2の保存に失敗しました。

データベースの中身は示しませんが、Orderテーブルが保存されていないことが確認できました。


1データベース内のトランザクションは、コネクション単位だと思うのですが、あえて各モデルオブジェクトからアクセスできるようにしているのは、ある種の冗長を持たせているようです。