ActiveRecordを使ってみる
ActiveRecordはRubyOnRailsのO/Rマッパーとして有名ですが、Railsに依存しているわけではなく単体で使えます。
DBを用意する
なにはともあれ、DBがないと始まらないので適当に用意します。create database ar;
use ar;
create table users (
id int unsigned not null auto_increment,
name varchar(50),
age int,
primary key (id)
);
ユーザ名と年齢を管理するテーブルってことで。
DBへの接続とマッピング
require 'rubygems'
require_gem 'activerecord'
# ARで接続
ActiveRecord::Base.establish_connection(
:adapter => 'mysql',
:host => 'localhost',
:username => 'ユーザ名',
:password => 'パスワード',
:database => 'ar',
:socket => '/tmp/mysql.sock'
)
# マッピングクラスを定義
class User < ActiveRecord::Base
end
データベースへのコネクションはActiveRecord::Base.establish_connectionで作られます。特にむずかしいところはないですね。socketは必ず書く必要はないです。書かない場合は/tmp/mysql.sockを見にいくのかな。
ActiveRecord::Baseを継承することでマッピングクラスを定義することができます。クラス名がUserusersテーブルへのマッピングを定義していることになります。クラスを単数形、テーブル名を複数形にすることで自動的にマッピングされます。いわゆるCoC(Convention over Configuration)ってやつです。
データの保存
newメソッド、もしくはcreateメソッドを使用し、ARオブジェクトを作成することでDBにデータを保存(Insert)することができます。newとcreateの違いは、newは明示的にsaveメソッドを実行する必要があり、createは自動的にsaveメソッドも実行されます。
ARオブジェクトを作成
user = User.new(:name => “UK”, :age => 20)
user.save # DBに格納
# ARオブジェクトを作成
user = User.create(:name => "UK", :age =>20) # この時点でDBに格納される
データの検索

データの検索(Select)にはfindメソッドを使用します。他にもfind_allとか、find_by_sqlとかあるけどとりあえずfindのみの説明で。ちなみにfind_allは非推奨的な雰囲気。find(:all)を使えってことですね。
DEPRECATION WARNING: find_all is deprecated and will be removed from Rails 2.0 ( use find(:all, ...)) See http://www.rubyonrails.org/deprecation for details.
findメソッド
findメソッドの引数にIDを指定することで、該当するデータのオブジェクトが得られます。IDは複数指定することが可能でその場合の戻り値は配列です。:allを指定すると、 全件分のオブジェクトが得られます。こちらの場合も配列です。:firstを指定すると、先頭のオブジェクトを得られます。
p User.find(1) # ID = 1のオブジェクト
=> #"UK", "id"=>"1", "age"=>"20"}>
p User.find(1,4) # ID =1, ID = 4 のオブジェクトを格納した配列
=> [# "UK", "id"=>"1", "age"=>"20"}>,
# "bar", "id"=>"4", "age"=>"38"}>]
p User.find([2]) # ID = 2のオブジェクトを格納した配列
=> [# "hoge", "id"=>"2", "age"=>"40"}>]
p User.find(:all) # 全件分のオブジェクトを格納した配列
=> [# "UK", "id"=>"1", "age"=>"20"}>, # "hoge", "id"=>"2", "age"=>"40"}>, # "foo", "id"=>"3", "age"=>"14"}>, # "bar", "id"=>"4", "age"=>"38"}>]
p User.find(:first) # 先頭のオブジェクト
=> # "UK", "id"=>"1", "age"=>"20"}>
各カラムのデータにアクセスする場合は以下のようにします。
p User.find(1).name # UK
p User.find(1,4)[1].age # 38
p User.find([2])[0].name # hoge
p User.find(:all)[3].name # bar
p User.find(:first).age # UK
ActiveRecord::RecordNotFound
ちなみに存在しないID(この場合だと5とか)を指定すると例外(ActiveRecord::RecordNotFound)が発生するので注意して下さい。条件文の指定
普通に考えて、データへのアクセスにIDの指定とか不便すぎますよね。次は引数に条件文を与えてみたいと思います。SQLで言う、where文に該当します。
p User.find(:first, :conditions => {:name => "UK"})
=> #"UK", "id"=>"1", "age"=>"20"}>
p User.find(:first, :conditions => {:name =>"hoge", :age => 40})
=> # "hoge", "id"=>"2", "age"=>"40"}>
p User.find(:first, :conditions => {:name => "aaa"})
=> nil
条件文は:conditionsで指定します。1番目のfindはnameカラムのデータが”UK”に該当するものから先頭のオブジェクトを返します。ここで:firstの指定を:allに変更すると:conditionsに該当する全てのオブジェクトを配列で返します。
User.crate(:name => "UK", :age => 20) # ID = 1と同じデータを保存
p User.find(:fist, :conditions => {:name => "UK"}
=> #"UK", "id"=>"1", "age"=>"20"}>
p User.find(:all, :conditions => {:name => "UK"}
=> [# "UK", "id"=>"1", "age"=>"20"}>, # "UK", "id"=>"5", "age"=>"20"}>]
パラメータによるサニタイズ
上記のやり方だとパラメータを直接クエリに挿入します。いわゆるサニタイズと言ったものは行っていません。それだと流石にまずいと思うので以下のようにパラメータ使用します。
p User.find(:all, :conditions => ["name = ? and age = ?", "UK", 20])
=> [#"UK", "id"=>"1", "age"=>"20"}>, # "UK", "id"=>"5", "age"=>"20"}>]
このようにしてパラメータを指定することで”UK”と20はサニタイズされます。
名前付きパラメータ
パラメータを使用している場合、クエスチョンマークが増えすぎるとわかりにくくなる時があります。その場合は名前つきパラメータを使用します。クエスチョンマークの代わりにシンボルを使用し、シンボルをキーにしたハッシュを渡します。
User.find(:all, :conditions => ["name = :name and age = :age", {:name => "UK", :age => 20}])
=> [#"UK", "id"=>"1", "age"=>"20"}>, # "UK", "id"=>"5", "age"=>"20"}>]
データの上書き保存
データの上書き保存(Update)するにはfindメソッドなどでARオブジェクトを作成、操作しsaveメソッドで行ないます。
user = User.find(:first, :conditions => {:name => "hoge"})
p user.age # 40
user.age =69
user.save
User.find(:first, :conditions => {:name => "hoge"})
=> [#"hoge", "id"=>"2", "age"=>"69"}>]
データの削除
データの削除(Delete)はdeleteメソッドや、destroyメソッドを使用します。
User.delete(1) # ID = 1を削除
user = User.find(:first, :conditions => {:name => "UK"})
user.destory # name が "UK"のデータを削除
雑感
とりあえずざっとActiveRecordに触れてみましたが、DBをRubyのオブジェクトのように扱えるのは思いの外気持ちがいいですなー。ただ、プライマリーキー名が”id”固定なところやテーブル名を複数形云々のあたりが気になる人はいるのかなぁと。あと複合キーとかどうするんだーとか。そういうところを気にしないので済むのであれば、ActiveRecordはかなり良いと思います。それにしてもコードがみづらいことこの上ないですね・・・そのうちなんとかしたいところです。