ActiveRecord::Base.establish_connection
WARNING
Despite the 1.1.0 version number, this gem is not quite production ready. Various people have experienced problems using the 1.0.0 version. A solution was found to deal with this issue but it has not been fully tested, so please subscribe to the forum or RubyForge news for any updates.
What
ActiveRecord models are allowed one connection to a database at a time, per class. Ruby on Rails sets up the default connection based on your database.yml configuration to automatically select development, test or production.
But, what if you want to access two or more databases – have 2+ connections open – at the same time. ActiveRecord requires that you subclass ActiveRecord::Base
.
That prevents you doing migrations from one database to another. It prevents you using one set of model classes on two or more databases with the same schema.
Magic Multi-Connections allows you to write your models once, and use them for multiple database Rails databases at the same time. How? Using magical namespacing.
class Person < ActiveRecord::Base; end ActiveRecord::Base.establish_connection :production Person.connection # => production module ContactRepository establish_connection :contact_repo end ContactRepository::Person.connection # => contact_repo old_person = ContactRepository::Person.find_by_email(email) person = old_person.create_as(Person)
You do not have to redefine your models for the multi-connection module ContactRepository
, they are automatically picked up for you. Magically.
TODO: Example about Associations
Issues
Despite the 1.1.0 version of this gem there are still a number of issues with this gem:
- Single Table Inheritance is not currently supported
- No connection pooling for alternate databases
Any help would be greatly appreciated
Installing
sudo gem install magic_multi_connections
Rails: Add the following to the bottom of your environment.rb
file
require 'magic_multi_connections'
Ruby scripts: Add the following to the top of your script
require 'rubygems' require 'magic_multi_connections'
Demonstration with Rails
A quick demonstration within Rails to provide a parallel “private” database for an application.
1. Create rails app
Using sqlite3 here, but use your preferred db:
> rails privacy -d sqlite3 > cd privacy > ruby script/generate model Person > cp config/environments/development.rb config/environments/private.rb
The last line allows us to play with our private database within the console and rake tasks.
2. Edit config/database.yml and add our private database:
Add the following to the bottom of config/database.yml
private: adapter: sqlite3 database: db/private.sqlite3
3. Create a database schema
Edit db/migrate/001_create_people.rb
class CreatePeople < ActiveRecord::Migration def self.up create_table :people do |t| t.column :name, :string end end def self.down drop_table :people end end
From the command line, migrate this to our development and private databases:
> rake db:migrate > rake db:migrate RAILS_ENV=private
4. Add some data to databases
> ruby script/console development >> Person.create(:name => 'Nic') >> Person.create(:name => 'Banjo') >> exit > ruby script/console private >> Person.create(:name => 'Super Magical Nic') >> exit
Now it should be obvious which database our app is accessing.
5. Update environment.rb
Edit config/environment.rb to include the library and create the Private module.
Add the following to the end of the file.
require "magic_multi_connections" module Private establish_connection :private end
This tells the Private module that any model class that is requested will be assigned a connection to the private database, as defined in the config/database.yml specification.
6. Setup a controller
Create a people controller with a index action
> ruby script/generate controller people index
Edit your controller app/controllers/people_controller.rb
class PeopleController < ApplicationController before_filter :check_private def index @people = @mod::Person.find(:all) end private def check_private @mod = params[:private] ? Private : Object end end
The check_private action is a hack to demonstrate one method for selecting the database. In reality, a stupid one for hiding a “private” database, but you get the point.
After check_private is called, @mod is either the Object (default) module or the Private module. The Person class is accessible through either of them.
Yes, @mod::Person
is uglier than just Person
. Sorry.
7. Setup the index.rhtml view
Edit app/views/people/index.rhtml
<h1><%= @mod::Person %></h1> <h2><%= @mod::Person.active_connection_name %></h2> <ol> <% @people.each do |person| -%> <li><%= "#{person.name} - #{person.class}" %></li> <% end -%> </ol>
8. Test our multi-connection Rails app
Launch the app
> mongrel_rails start
In your browser, go to http://localhost:3000/people and see the list of people: Nic and Banjo.
Now, to see the private database, go to http://localhost:3000/people?private=1 and see the private list. Its so private, I won’t show it here.
Note: you may need to refresh the private url to see the results. Perhaps Rails is caching the SQL even though its a different connection to a different database. If you know what’s happening here, please email me or the forum. Thanks.
9. End
There ends our example of a Rails application using one model class to access multiple databases cleanly.
Pre-existing modules
In Rails, model files are placed in the app/models folder. If you place them in a subfolder, say app/models/admin, then those model classes are access via module namespaces.
So, app/models/admin/page.rb represents the Admin::Page
class.
Magic Multi-Connections works for these model classes as well.
Admin.establish_connection :admin_dev Admin::Page.active_connection_name # => "Admin::Page"
Related articles
- Original blog article – Magic Multi-Connections: A “facility in Rails to talk to more than one database at a time
- Discussed by DHH
Dr Nic’s Blog
http://www.drnicwilliams.com/ – for future announcements and other stories and things.
Forum
Discussion about the Magic Multi-Connections is on the Magic Models forum:
http://groups.google.com/group/magicmodels
Licence
This code is free to use under the terms of the MIT licence.
Contact
Comments are welcome. Send an email to Dr Nic Williams
ActiveRecord是Rails提供的一个对象关系映射(ORM)层,从这篇开始,我们来了解Active Record的一些基础内容,连接数据库,映射表,访问数据等。
Active Record使用基本的ORM模式:表映射成类,行映射成为对象,列映射成对象的属性。与很多大量使用配置的ORM库不同,Active Record最小化了配置。想象一下,有一个使用Active Record的程序把Mysql数据库中的orders表转换到类,通过制定的ID查找到order,设定order的名称,然后保存回数据库:
require "rubygems" require_gem "activerecord" ActiveRecord::Base.establish_connection(:adapter => "mysql", :host => "localhost", :database => "railsdb") class Order < ActiveRecord::Base end order = Order.find(123) order.name = "Dave Thomas" order.save
在上面的例子里不需要任何配置,Active Record为我们做了这些事情,下面我们来看看ActiveRecord是怎样工作的。
表和类
当你创建了一个ActiveRecord::Base类的子类,Active Record假定表名是复数的,而类名是单数的,当类名包括多个单词时,表名被假定为单词间带有下划线,复数形式不规则,例如:
类名 表名 类名 表名
Order orders LineItem line_items
TaxAgency tax_agencies Person people
Diagnosis diagnoses Quantity quantities
Batch batches Datum data
默认的,Active Record的表名是复数的,类名是单数的,如果你不太习惯,可以通过设置一个全局标记来禁用它,在config目录的environment.rb文件中设置:
ActiveRecord::Base.pluralize_table_names = false
单复数规则可以对付大部分情况,对于一些特殊情况,Active Record允许我们覆盖默认的生成的表名,使用set_table_name命令,例如:
class Sheep < ActiveRecord::Base set_table_name "sheep" # Not "sheeps" end class Order < ActiveRecord::Base set_table_name "ord_rev99_x" # Wrap a legacy table... end
Active Record抽象了数据库连接的概念,帮助应用程序来处理底层的数据库链接的细节,作为替代,Active Record使用通用的调用,将细节委托给一组数据库适配器。
可以使用establish_connection( )方法来制定连接,下面的例子创建了一个mysql数据库连接,数据库的名字是railsdb,服务器的Host名为dbserver.com,用户名为railsuser,密码为railspw。
ActiveRecord::Base.establish_connection( :adapter => "mysql", :host => "dbserver.com", :database => "railsdb", :username => "railsuser", :password => "railspw" )
Active Record支持DB2,MySql,Oracle,Postgres,SqlServer,以及SqlLite,每一种数据库适配器在链接的参数上都有一些细小的差别,下表列出了常用的参数:
注意Oracle适配器的名字为oci。
数据库连接和Model类是关联的,每个类都从父类那里继承了链接,ActiveRecord::Base作为所有的Active Record类的父类,设置这里的数据库连接就给所有的活动记录类设置了链接,当然,如果需要的话,你也可以复写(override)链接配置。
下面的例子里,我们的大多数表都在MySql数据库中,库名为online,由于一些历史原因,customers表在名为backend的数据库中,
ActiveRecord::Base.establish_connection( :adapter => "mysql", :host => "dbserver.com", :database => "online", :username => "groucho", :password => "swordfish") class LineItem < ActiveRecord::Base # ... end class Order < ActiveRecord::Base # ... end class Product < ActiveRecord::Base # ... end class Customer < ActiveRecord::Base # ... end Customer.establish_connection( :adapter => "mysql", :host => "dbserver.com", :database => "backend", :username => "chicho", :password => "piano")
在我们前面所写的depot程序中,我们没有使用establish_connection方法,而是在config/database.yaml文件中指定了数据库连接的参数信息,对于大多数rails程序来说,这是首选的方式,不仅因为将配置信息和代码分离,而且在测试和部署时也能带来方便,上面的表格里列出的参数都可以应用在YAML文件中,这一点我们在前面的配置文件一节已经有介绍。
最后,如果你通过一个标记访问establish_connection(),Rails会在database.yaml文件中查找名字对应的配置节,来获取链接的参数,这样就可以将所有的数据库连接配置从代码中分离出来。
config = YAML::load(File.open('config/database.yml')) ActiveRecord::Base.establish_connection(config["production"])