2014-10-26 61 views
3

Rails应用程序中,我有这样的代码在纯Ruby:仅在一个块中建立到另一个数据库的连接?

class LinkCreator 
    attr_accessor :animal 

    def initialize(animal:) 
    @animal = animal 
    end 

    def call 
    "something#{link_id}" 
    end 

    private 

    def link_id 
    connection.execute(sql_request).first.first 
    end 

    def sql_request 
    "SELECT field FROM table WHERE field_id = '#{field_id}' LIMIT 1" 
    end 

    def field_id 
    animal.field_id 
    end 

    def connection 
    ActiveRecord::Base.establish_connection(
     adapter: "mysql", 
     host:  ENV["MYSQL_HOST"], 
     username: ENV["MYSQL_USERNAME"], 
     password: ENV["MYSQL_PASSWORD"], 
     database: ENV["MYSQL_DB_NAME"] 
    ).connection 
    end 
end 

正如你所看到的,这不是一个模型,但只有一个简单的类。问题在于,activerecord的连接被更改,其他请求将在新连接上执行。

是否可以仅在一个块中建立连接并返回到旧连接。我知道我可以建立另一个连接,但这对性能来说非常糟糕。

回答

7

如果你一直在database.yml

development: 
    adapter: mysql2 
    other stuff... 

db_2: 
    adapter: mysql2 
    other stuff.. 

other_envs: 
..... 

所有的数据库连接,然后创建一个类

class OtherDB < ActiveRecord::Base 
    establish_connection(:db_2) 
end 

从你的控制器,你可以访问就像

OtherDB.table_name = "table_name" 
OtherDB.first 

入住这将是很好我的博客http://imnithin.in/multiple-database.html

+1

这里最重要的区别在于连接被比其他的对象进行所有普通对象的基类 - 因此可以单独保留与普通对象的连接。这也将配置保存在一个共同的地方,我认为可以保持备用数据库连接的开放,这对于性能来说可能是一件好事,这取决于您的使用条件。 – DGM 2014-10-26 15:14:25

0

这可能有助于使用实例变量来存储连接。类似这样的:

def connection 
    @connection ||= ActiveRecord::Base.establish_connection(
    adapter: "mysql", 
    host:  ENV["MYSQL_HOST"], 
    username: ENV["MYSQL_USERNAME"], 
    password: ENV["MYSQL_PASSWORD"], 
    database: ENV["MYSQL_DB_NAME"] 
).connection 
end 

这种方式在未来的连接尝试上检索现有连接,而不是建立一个新的连接。

4

您可以在块中执行一些查询。首先,定义一些将扩展ActiveRecord的模块,如下所示。这是生产中用于每个请求更改数据库连接的代码的一部分,以及临时切换数据库以在另一个数据库中执行某些查询。

# RAILS_ROOT/lib/connection_switch.rb 
module ConnectionSwitch 
    def with_db(connection_spec_name) 
    current_conf = ActiveRecord::Base.connection_config 

    begin 
     ActiveRecord::Base.establish_connection(db_configurations[connection_spec_name]).tap do 
     Rails.logger.debug "\e[1;35m [ActiveRecord::Base switched database] \e[0m #{ActiveRecord::Base.connection.current_database}" 
     end if database_changed?(connection_spec_name) 

     yield 
    ensure 
     ActiveRecord::Base.establish_connection(current_conf).tap do 
     Rails.logger.debug "\e[1;35m [ActiveRecord::Base switched database] \e[0m #{ActiveRecord::Base.connection.current_database}" 
     end if database_changed?(connection_spec_name, current_conf) 
    end 

    end 

    private 
    def database_changed?(connection_spec_name, current_conf = nil) 
    current_conf = ActiveRecord::Base.connection_config unless current_conf 
    current_conf[:database] != db_configurations[connection_spec_name].try(:[], :database) 
    end 

    def db_configurations 
    @db_config ||= begin 
     file_name = "#{Rails.root}/config/database.yml" 
     if File.exists?(file_name) || File.symlink?(file_name) 
     config ||= HashWithIndifferentAccess.new(YAML.load(ERB.new(File.read(file_name)).result)) 
     else 
     config ||= HashWithIndifferentAccess.new 
     end 

     config 
    end 
    end 
end 
ActiveRecord.send :extend, ConnectionSwitch 

现在你可以如下使用它:

ActiveRecord.with_db("db_connection_name") do 
    # some queries to another db 
end 
+1

不错的一段代码。你有没有考虑将它作为宝石发布? – sufleR 2015-04-10 08:46:44

+0

@ sufleR,不,我不知道。也许这是值得的。 – blelump 2015-04-14 09:44:40

1

已经找到了Rails的代码库中最简单的例子,在activerecord/test/support/connection_helper.rb和稍微适应:

def with_another_db(another_db_config) 
    original_connection = ActiveRecord::Base.remove_connection 
    ActiveRecord::Base.establish_connection(another_db_config) 
    yield 
ensure 
    ActiveRecord::Base.establish_connection(original_connection) 
end 

使用(因为你有another_db:部分在您的database.yml):

with_another_db(ActiveRecord::Base.configurations['another_db']) do 
    ActiveRecord::Base.connection.execute("SELECT 'Look ma, I have changed DB!';") 
end 
0

我使用Heroku的DATABASE_URL采取环境变量来连接到不同的数据库:

class Database 
    def self.development! 
    ActiveRecord::Base.establish_connection(:development) 
    end 

    def self.production! 
    ActiveRecord::Base.establish_connection(ENV['PRODUCTION_DATABASE']) 
    end 

    def self.staging! 
    ActiveRecord::Base.establish_connection(ENV['STAGING_DATABASE']) 
    end 
end 

如:

Database.production!; puts User.all.map(&:name) 
Database.staging!; puts User.all.map(&:name) 
相关问题