Ectoで複数のRepoを使う
なにがしたい
例えば、レガシーなシステムを徐々にモダンにしていきたいので、古いDBと新しいDBを 共存させたいとします。要は、異なる接続先の2つ3つのDBを使いたい。
どうする
Repo
を2つ作ります。
以下、Phoenixのディレクトリ構成に合わせて説明します。
普通にmix phoenix.new
すると、lib/my_app/repo.ex
が作られます。
defmodule MyApp.Repo do use Ecto.Repo, otp_app: :my_app end
このRepo
はEctoの各種Mixタスクでデフォルトとして使われるもので、
接続先はconfig/dev.exs
などに書かれてます。例えばこんな。
# Configure your database config :my_app, MyApp.Repo, adapter: Ecto.Adapters.MySQL, username: "joe", password: "hogehoge", database: "my_app_dev", hostname: "192.168.33.10", size: 10
EctoのRepo
はデータの保存場所を抽象化してくれるので、どこに保存するのか、
どんな形で保存されているのか、何のRDBMSが使われているのか、などは意識しなくてよく、
単にRepo.insert!
とかすればよいというわけですね。便利便利。
ではこれを複数持つにはどうするかというと、素直に増やせばいいです。lib/my_app
にanother_repo.ex
を作ります。
# lib/my_app/another_repo.ex defmodule MyApp.AnotherRepo do use Ecto.Repo, otp_app: :my_app end
config/dev.exs
に、AnotherRepo
の接続先設定を追加します。適宜config/test.exs
などにも書いといてください。
# config/dev.exs # 中略 # Configure your database config :my_app, MyApp.Repo, adapter: Ecto.Adapters.MySQL, username: "joe", password: "hogehoge", database: "my_app_dev", hostname: "192.168.33.10", size: 10 config :my_app, MyApp.AnotherRepo, adapter: Ecto.Adapters.MySQL, username: "joe", password: "ebimayo", database: "my_app_another_dev", hostname: "192.168.33.11", size: 10
これで準備できました。migrateなどを実行するとき、
$ mix ecto.gen.migration add_user_table -r MyApp.AnotherRepo $ mix ecto.migrate -r MyApp.AnotherRepo
のようにr
オプションでどっちを使うか指定できます。無指定だとMyApp.Repo
が使われます。
あとは、モデルの中などでRepo.insert!
かAnotherRepo.insert!
かを使い分けたりすれば、
「このモデルはあっちのDBへ〜」とか「この条件を満たしたらこっち〜」とかできます。
便利便利。
追記
lib/my_app.ex
に書くの忘れてました。start/2
のchildren
にworker(MyApp.AnotherRepo, [])
を足しましょう。
children = [ # Start the endpoint when the application starts supervisor(MyApp.Endpoint, []), # Start the Ecto repository worker(MyApp.Repo, []), worker(MyApp.AnotherRepo, []) # Here you could define other workers and supervisors as children # worker(MyApp.Worker, [arg1, arg2, arg3]), ]
これ忘れると、mix test
とかでこんなエラー出ます。
** (exit) exited in: GenServer.call(MyApp.AnotherRepo.Pool, {:query, :begin, &MyApp.AnotherRepo.log/1, [timeout: 5000]}, 5000) ** (EXIT) no process