トランザクション

トランザクションとは、関連のあるデータベースの更新処理を一つにまとめたもの。BEGINでトランザクション処理を開始し、COMMITで一連の更新処理の結果をデータベースに反映する。また途中で問題が生じた場合はROLLBACKでデータベースをトランザクション処理を行う前の状態に戻すことができる。これらのことにより、全て成功するか、全て失敗するかの二択であることが保証され、データの整合性を保つことができる。


sqlite3-rubyトランザクション処理を行うには以下のようにする。

db.transaction # トランザクションを開始する
begin
  db.execute('insert into test_a values(?, ?)', *data_a)
  db.execute('insert into test_b values(?, ?)', *data_b)
  db.execute('insert into test_c values(?, ?)', *data_c)
  db.commit    # 処理を確定する
rescue
  db.rollback  # 例外が発生したら処理を取り消す
end

ブロックを使って書くこともできる。

db.transaction do
  db.execute('insert into test_a values(?, ?)', *data_a)
  db.execute('insert into test_b values(?, ?)', *data_b)
  db.execute('insert into test_c values(?, ?)', *data_c)
end


本当に例外が発生したら処理が取り消されるか試してみる。

#!/usr/bin/ruby
require 'sqlite3'

def transaction_test(data)
  db = SQLite3::Database.new('test.db')
  db.execute('delete from test') 

  begin
    db.transaction do
      data.each do |d|
        raise if d.nil?
        db.execute('insert into test values(?, ?)', *d)
      end
      puts 'insert ok'
    end
   rescue => e
     puts 'insert error'
  end

  db.close

  db = SQLite3::Database.new('test.db')
  p db.execute('select * from test')
end

data = [
  ['b', 'b@hoge.com'],
  ['c', 'c@hoge.com'],
  ['d', 'd@hoge.com'],
]

transaction_test(data)
puts
transaction_test(data << nil) 
insert ok
[["b", "b@hoge.com"], ["c", "c@hoge.com"], ["d", "d@hoge.com"]]

insert error
[]