我有一个JRuby应用程序,我试图为生活在两个完全不同的服务器上的数据库创建has_many through:
关系。我知道连接不能在不同服务器上的表之间工作。我想要的是模拟连接,以便使用该模型的开发人员不必(如)知道跨服务器连接。
此设置还有一些其他复杂性:
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 :(得分:3)
刚刚为您快速浏览了一下,这里是回购:https://github.com/beneggett/many_db_example
在回购中,我刚刚在我的本地做了两个不同的数据库,但它不重要,主体是相同的:
这似乎对我有用:
告诉帐户有关account_dim_users连接表关联的信息,但是手动映射has_many到/ habtm。
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
这种方法允许您仍以标准方式使用Ruby对象:
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 Relations或类似功能,从而启用更多功能:
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
答案 1 :(得分:1)
使用dblink http://www.postgresql.org/docs/9.3/static/dblink.html,您将在同一个数据库中拥有另一个表。问题解决了。