2014-09-26 40 views
4

我有一个JRuby应用程序,我试图创建has_many through:关系生活在两个完全不同的服务器上的数据库的has_many through:关系。我知道连接不能在不同服务器上的表上工作。我想要的是模拟连接,以便使用模型的开发人员不必(知道)跨服务器连接。如何获得has_many通过:跨服务器工作

有在此设置一些额外的复杂性:

  • 远程数据库是只读的
  • 在远程数据库中的表名和主键不按轨道命名约定。 (远程数据库是Data Warehouse
  • 我希望能够使用该模型,就像其中有一个has_and_belongs_to_many

我已经考虑写我自己的自定义association,但是这是一个有点复杂,我无法找到任何导游或任何真正的起点,其他则通过Rails代码读取。

有没有简单的方法来做到这一点,我失踪了?

建立一个自定义的ActiveRecord协会是最好的办法吗?如果是的话,我从哪里开始?

代码类似于我的设置:

config/database.yml

development: 
    adapter: postgresql 
    encoding: unicode 
    database: main 
    username: username 
    password: password 
    host: localhost 
    pool: 5 

remote_development: # Read only 
    adapter: jdbcmssql 
    driver: com.microsoft.sqlserver.jdbc.SQLServerDriver 
    url: 'jdbc:sqlserver://foo.com;databaseName=main' 
    username: username 
    password: password 

app/models/account.rb

class Portfolio < ActiveRecord::Base 
    #has_and_belongs_to_many :dim_users, join_table: :accounts_dim_user 
end 

app/models/remote_model_base.rb

class RemoteModelBase 
    require "#{Rails.root}/lib/sqljdbc4.jar" 
    self.abstract_class = true 
    establish_connection "remote_#{Rails.env}".to_sym 
    after_initialize :readonly! 
end 

app/models/dim_user.rb

class DimUser < RemoteModelBase 
    self.table_name = 'DimUser' 
    self.primary_key = 'dwidDimUser' 

    #has_and_belongs_to_many :accounts, join_table: :accounts_dim_user 
end 

config/schema.rb

ActiveRecord::Schema.define(version: 20140925200106) do 

    create_table "accounts", force: true do |t| 
    t.string "name" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
    end 

    create_table "accounts_dim_user", force: true, id: false do |t| 
    t.integer "dwidUser" 
    t.integer "account_id" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
    end 

    # Defined in the remote database but it might look something like this 
    # create_table "DimUser" do |t| 
    # t.integer dwidUser 
    # # ... 
    # end 
+0

我发现宝石[ST别处(https://github.com/ briandoll/st-elsewhere),但我担心它已经5岁了。 – Nate 2014-09-26 22:03:28

回答

3

通过一个快速的场景为你刚跑,这里的回购:https://github.com/beneggett/many_db_example

在回购协议,我只是做了我的地方2分不同的数据库的,但它并不重要,主要是相同的:

这似乎也为我工作:

告诉有关account_dim_users帐户连接表关联,但手动映射通过/ HABTM的的has_many。

class Account < ActiveRecord::Base 
    has_many :account_dim_users 

    def dim_users 
    account_dim_users.map {|account_dim_user| DimUser.find_by(dwidUser: account_dim_user.dwidUser) } 
    end 
end 

这很重要,因为正如你所知,标准连接不起作用;但是通过模型映射它可以正常工作。

AccountDimUser连接表看起来标准(我明确地映射按键)

class AccountDimUser < ActiveRecord::Base 
    has_many :accounts 
    has_many :dim_users, primary_key: :dwidUser, foreign_key: :dwidUser 

end 

手动映射account_dim_users协会,以及手工绘制的账户关联

class DimUser < ActiveRecord::Base 
    establish_connection "other_db".to_sym 
    after_initialize :readonly! 
    self.table_name = 'DimUser' 
    self.primary_key = 'dwidUser' 

    def account_dim_users 
    AccountDimUser.where(dwidUser: self.dwidUser) 
    end 

    def accounts 
    account_dim_users.map {|account_dim_user| Account.find(account_dim_user.account_id) } 
    end 
end 

这种方法可以让你仍然可以使用您的红宝石物品的标准方式:

a = Account.first 
    Account Load (0.6ms) SELECT "accounts".* FROM "accounts" ORDER BY "accounts"."id" ASC LIMIT 1 
=> #<Account:0x00000102d263d0> { 
      :id => 1, 
     :name => "New account", 
    :created_at => Mon, 29 Sep 2014 15:07:07 UTC +00:00, 
    :updated_at => Mon, 29 Sep 2014 15:07:07 UTC +00:00 
} 

-

a.account_dim_users 
=> #<ActiveRecord::Associations::CollectionProxy [#<AccountDimUser id: 1, dwidUser: 1, account_id: 1, created_at: "2014-09-29 15:08:47", updated_at: "2014-09-29 15:08:47">, #<AccountDimUser id: 3, dwidUser: 5, account_id: 1, created_at: "2014-09-29 15:24:17", updated_at: "2014-09-29 15:25:06">]> 

-

a.dim_users 
    AccountDimUser Load (0.3ms) SELECT "account_dim_users".* FROM "account_dim_users" WHERE "account_dim_users"."account_id" = $1 [["account_id", 1]] 
    DimUser Load (0.9ms) SELECT "DimUser".* FROM "DimUser" WHERE "DimUser"."dwidUser" = 1 LIMIT 1 
    DimUser Load (0.3ms) SELECT "DimUser".* FROM "DimUser" WHERE "DimUser"."dwidUser" = 5 LIMIT 1 
=> [ 
    [0] #<DimUser:0x0000010981af10> { 
      :id => 1, 
     :dwidUser => 1, 
    :created_at => Mon, 29 Sep 2014 15:06:44 UTC +00:00, 
    :updated_at => Mon, 29 Sep 2014 15:06:44 UTC +00:00 
    }, 
    [1] #<DimUser:0x00000109838b00> { 
      :id => 5, 
     :dwidUser => 5, 
    :created_at => Mon, 29 Sep 2014 15:23:01 UTC +00:00, 
    :updated_at => Mon, 29 Sep 2014 15:23:01 UTC +00:00 
    } 
] 

-

d = DimUser.first 
    DimUser Load (0.5ms) SELECT "DimUser".* FROM "DimUser" ORDER BY "DimUser"."dwidUser" ASC LIMIT 1 
=> #<DimUser:0x0000010990aad8> { 
      :id => 1, 
    :dwidUser => 1, 
    :created_at => Mon, 29 Sep 2014 15:06:44 UTC +00:00, 
    :updated_at => Mon, 29 Sep 2014 15:06:44 UTC +00:00 
} 

-

d.account_dim_users 
    AccountDimUser Load (0.5ms) SELECT "account_dim_users".* FROM "account_dim_users" WHERE "account_dim_users"."dwidUser" = 1 
=> #<ActiveRecord::Relation [#<AccountDimUser id: 1, dwidUser: 1, account_id: 1, created_at: "2014-09-29 15:08:47", updated_at: "2014-09-29 15:08:47">]> 

-

d.accounts 
    AccountDimUser Load (0.5ms) SELECT "account_dim_users".* FROM "account_dim_users" WHERE "account_dim_users"."dwidUser" = 1 
    Account Load (0.4ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT 1 [["id", 1]] 
=> [ 
    [0] #<Account:0x000001099788d0> { 
      :id => 1, 
      :name => "New account", 
    :created_at => Mon, 29 Sep 2014 15:07:07 UTC +00:00, 
    :updated_at => Mon, 29 Sep 2014 15:07:07 UTC +00:00 
    } 
] 

在处理大量记录时,可能会对此进行一些优化,但这是一个很好的基础。

另一种方法可能是做关联表本身的查询,如:

def find_dim_user 
    DimUser.find_by(dwidUser: self.dwidUser) 
    end 

但是我非常喜欢我提出的第一种方式,因为它可以让你做你一般的Ruby方法链接方法关联。

任何其他问题,让我知道!

编辑:你可以改变地图功能使用Active Record的关系或类似的还有,实现更多的功能:

class Account < ActiveRecord::Base 
    has_many :account_dim_users 

    def dim_users 
    dim_user_ids = account_dim_users.map {|account_dim_user| account_dim_user.dwidUser } 
    DimUser.where(dwidUser: dim_user_ids) 
    end 
end 
+0

这看起来像我正在寻找的东西。我会试试看,稍后再回来。 – Nate 2014-09-29 16:19:03

+1

这是一个很好的解决方案。一个缺点是跨服务器关联如'Account.first.dim_users'返回'Array'而不是'CollectionProxy'。这意味着你不能通过'a.dim_users << DimUser.find(1)添加'dim_users'; a.save'。但是,这可能是我最好的解决方案,不需要创建一个复杂且耗时的自定义关联。我可能会建议可选地在返回的数组中添加'freeze',以便修改它会引发错误。 – Nate 2014-09-29 19:13:45

+0

当然,它也可能更具可扩展性,而不是映射,而是使用Active记录来查找集合。请参阅 – 2014-09-29 19:48:18

相关问题