Help us understand the problem. What is going on with this article?

Active Recordを単体で使ってRailsで複数DB接続する

More than 1 year has passed since last update.

はじめに

Rails6では、標準で複数DB接続がサポートされるようですね。。。早く使いたい。。。
Rails5のプロジェクトで、別DBから情報を引っ張ってきたいという要件があったので、試してみました。

ポイント

複数DBを扱うに当たって、めんどくさいことはマイグレーションでしょう。
今回は、別サービスから別サービスのDBへ単純に参照したいという要件なので、シンプルに行きます。
マイグレーション自体も、頑張れば複数DB用に管理できますが、結構めんどくさいです。
あと、Railsに慣れていると勘違いしてしまうかもしれませんが、Active Record自体はマイグレーションは必要ありません。
接続情報さえあれば、基本的にはいつも通り使えます。

データベース

今回は、こんな感じのテーブルがある感じです。

mysql> desc users;
+-------------------+---------------------+------+-----+---------+----------------+
| Field             | Type                | Null | Key | Default | Extra          |
+-------------------+---------------------+------+-----+---------+----------------+
| id                | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| name              | varchar(255)        | NO   |     | NULL    |                |
| email             | varchar(255)        | NO   | UNI | NULL    |                |
| email_verified_at | timestamp           | YES  |     | NULL    |                |
| password          | varchar(255)        | NO   |     | NULL    |                |
| remember_token    | varchar(100)        | YES  |     | NULL    |                |
| created_at        | timestamp           | YES  |     | NULL    |                |
| updated_at        | timestamp           | YES  |     | NULL    |                |
+-------------------+---------------------+------+-----+---------+----------------+

Gemを入れる

今回は、実験的にシンプルなRubyプロジェクトからDBをActive Recordを使って参照してみます。
あと、適当にMysql2を入れときます。テスト用に立てたMariaDBに繋いでみたんですが、普通に繋がりますね。

Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'activerecord'
gem 'mysql2'

別DB接続用のクラスを作る

それでは、やっていきます。
ActiveRecord::Base#establish_connectionに接続情報を渡してやれば、使えるわけですが
ベストプラクティスとしては、やはり別DB接続用の抽象クラスを作っておくことでしょうか。

connector.rb
require 'bundler/setup'
require 'active_record'

class Connector < ActiveRecord::Base
  self.abstract_class = true
  con = {
    adapter: 'mysql2',
    host:    'your_db_host',
    username: 'root',
    password: 'password',
    database: 'test_db'
  }
  establish_connection(con)
end

したら、こいつを継承した具象クラスを作ります。
これに関しては、ActiveRecordの命名規則に従うなり、オプションでテーブル名を指定するなりしてやってください。

user.rb
require './connector.rb'

class User < Connector
  scope :id, -> (id) { where(id: id) if id.present? }
end

動作確認

irbで動作確認してみます。

> irb
irb(main):001:0> require './user.rb'
=> true
irb(main):002:0> User.count
=> 2
irb(main):003:0> User.first
=> #<User id: 1, name: "test", email: "test@test.org", email_verified_at: nil, password: "password", remember_token: nil, created_at: "2019-06-17 00:00:00", updated_at: nil>
irb(main):004:0> user = User.new(id: 3, name: 'from active record', email: 'active_record@test.org', password: 'password')
=> #<User id: 3, name: "from active record", email: "active_record@test.org", email_verified_at: nil, password: "password", remember_token: nil, created_at: nil, updated_at: nil>
irb(main):005:0> user.save!
=> true
irb(main):006:0> User.count
=> 3
irb(main):007:0> User.id(1)
=> #<ActiveRecord::Relation [#<User id: 1, name: "test", email: "test@test.org", email_verified_at: nil, password: "password", remember_token: nil, created_at: "2019-06-17 00:00:00", updated_at: nil>]>
irb(main):008:0> User.find(1).email
=> "test@test.org"

いいですね、普通に動きました。
普通にActive Recordとしても使えますし、コネクションだけ用意して生SQL叩くとかの使い方も考えられますね。

参考

ActiveRecordを単体で使うには
複数のDBにまたがるRailsの運用方法まとめ
ActiveRecordで生SQLを使いたいときに便利なメソッド達

HighGreat
PHP/Go/Ruby
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away