2014-10-26 53 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) 
相關問題