2011年6月8日水曜日

[Rails3] ActiveReocrdで外部DBを使う

実行環境:
Rails 3.0.4

Railsでは基本的に1つのDBを使用するように設計されています。デフォルトで使用するDBは config/database.yml 内で、実行モード(Rails_Env:development/test/production)別にDBが指定されていますが、これらのデフォルトのDB以外のDBに接続するやり方です。

用途としては例えば、他のシステムで使用しているユーザテーブルを参照する(いわゆるレガシーDBというやつですね)、アクセスログデータを別のサーバ上に保管する、などといった場合があるでしょう。

ここで想定する外部DB/テーブルの条件

  • 使用しているデータベースのアダプタがある
    MySQL, PostgreSQL, SQLite3, Oracle, DB2, SQL Server, ...

  • 使用するテーブルに、整数型の単独の主キーが存在する
  • Railsアプリケーションのサーバからアクセスが可能
    別サーバ上のSQLiteなどのSQLサーバになっていないDBは対象外
    (そんな場合はこんな方法も ⇒ 新規RailsアプリからレガシーなDBを参照する方法 - 今日とは違う明日


モデルを外部DBと結び付ける


1. モデル定義で外部DB接続を指定
普通のモデル定義の中でestablish_connectionというメソッドを呼び出します。connectionはシングルトンで動作するのでestablish_connectionが呼び出されると、デフォルトのDB接続の代わりに指定されたDB接続が使われます。
app/models/users.rb
class User < ActiveRecord::Base establish_connection( :adapter => "mysql", :host => "dbserver", :database => "dbname", :username => "user", :password => "opensesame" ) # ... end

テーブル名や主キー名を指定する
外部DBのテーブル名や主キー名がRailsの規約に沿っている場合は上記でOKですが、他システムのDBを参照する場合などテーブル名や主キー名が規約通りでない場合、使用するテーブル名や主キーのカラム名を指定してあげる必要があります
app/models/user.rb
class User < ActiveRecord::Base establish_connection( # ... ) set_table_name "USER_TBL" set_primary_key "UID" # ... end
こうするとデフォルトのDBのテーブルと同じように扱えます。
@user = User.find(1)

2. DB接続の設定をconfig/database.ymlに設定する
モデルの中にDB接続設定を直接記載するのではなくアプリケーションの設定として記載する方がスマートです。config/database.ymlの中にデフォルトのDB接続情報に加えて外部DBの接続情報を追加します。
config/database.yml
development: # ... test: # ... production: # ... legacy_db: adapter: mysql encoding: utf8 database: dbname username: user password: opensesame host: hostname

app/models/users.rb
class User < ActiveRecord::Base establish_connection(:legacy_db) # ... end

3. 外部DB接続の定義ファイルを独立させる
例えばconfig/legacy_database.ymlという別ファイルにDB接続の設定を保存するとする。
config/legacy_database.yml
adapter: mysql encoding: utf8 database: dbname username: user password: opensesame host: hostname

app/model/user.rb
class User < ActiveRecord::Base config_legacy = YAML.load_file(Rails.root.join('config/legacy_database.yml')).symbolize_keys establish_connection(config_legacy) # ... end

同じ外部DBに接続するモデルが複数ある場合
各モデルに上記の方法(1, 2, 3のいずれか)の通りに書けばアクセスはできるようになります。が、コネクションプーリングを有効に活用するという観点からは、外部DB接続する親となるモデルを作り、親モデルを継承させて各モデルを作る方がよいようです。

以下のようにすると、User1とUser2は接続先のDBは同じでも別々の接続を使います。(コネクションプールが2つ使われてしまいます。)
app/models/user1.rb
class User1 < ActiveRecord::Base establish_connection(:legacy_db) end
app/models/user2.rb
class User2 < ActiveRecord::Base establish_connection(:legacy_db) end
以下のように継承を使うとUser1とUser2は同じコネクションを使ってくれます。
app/models/legacy_base.rb
class LegacyBase < ActiveRecord::Base establish_connection(:legacy_db) end
app/models/user1.rb
class User1 < LegacyBase end
app/models/user2.rb
class User2 < LegacyBase end

0 件のコメント:

コメントを投稿