Rails Rails 项目连接第二个数据库小结

lingceng · May 14, 2018 · 2121 hits

Rails 项目连接第二个数据库小结

业务需要,Rails 项目需要接入第二个数据库,这里记录一些适配的细节。
环境:Rails 4.2.5 Ruby2.3.0 Mysql5.7.10

不同的 DB 配置

在 config/database.yml 添加第二个数据库的配置,添加环境做后缀,这样方便测试和上线。

data_source_development: &data_default
  adapter: mysql2
  encoding: utf8mb4
  reconnect: true
  database: data_source_development
  pool: 5
  username: root
  password: password
  host: localhost

data_source_test:
  <<: *data_default
  database: data_source_test

data_source_production:
  <<: *data_default
  database: data_source_production

创建一个继承自 ActiveRecord::Base 的类,设置为 abstract_class

class DataRecord < ActiveRecord::Base
  self.abstract_class = true
  establish_connection("data_source_#{Rails.env}".to_sym)
end

不按约定的表名

Rails 里表名需要是类名称下划线形式的复数,我接入的第二个数据库全是单数,还有前缀。 例如 Data::Player 需要对应到 data_player 表。需要做下面的配置:

class DataRecord < ActiveRecord::Base
  self.abstract_class = true
  # New added
  self.table_name_prefix = "data_"
  # New added
  self.pluralize_table_names = false
  establish_connection("data_source_#{Rails.env}".to_sym)
end

如果没有规律,可以通过self.table_name = 'table_name'来设置表名。

不同的 updated_at 或者 created_at

第二个数据库不是用 updated_at 来表示更新时间,而是用 last_updated,可以通过重写下面的方法设置:

def timestamp_attributes_for_update
  [:last_updated]
end

创建时间用 timestamp_attributes_for_create

存储的时间使用不同的时区

Rails 在存储和获取 datatime 时会根据配置做转换,当前的配置是存储 utc,显示北京时区。

config.time_zone = 'Beijing'
config.active_record.default_timezone = :utc

第二个数据库存储的是北京时间,所以需要处理第二个数据库的时区转换。 Rails 不能对时区做局部配置,所以处理麻烦一些,我使用的是在写入时 +8,读取时 -8。例如

def start_time
  attributes['start_time'] && (attributes['start_time'] - 8.hour)
end

def start_time=(value)
   converted = if value.present?
                 (value.is_a?(String) ? Time.zone.parse(value) : value) + 8.hour
               else
                 value
               end
   super(attr, converted)
end

当然这样每一个 model 重写有点麻烦,所以用了个Concern,检查并重写所有 datetime 类型字段。

bit 表达 boolean

Rails 里的 boolean 类型在 MySQL 里用tynyint(1),接入的第二个数据库是用的bit(1)。 我用了下面的方式做转换。

def hidden
  read_attribute(:hidden) == "\x01"
end

def hidden=(value)
  write_attribute(:hidden, ActiveRecord::Type::Boolean.new.type_cast_from_user(value).presence && "\x01")
end

其他

时区的转换有些繁琐,希望能找到更简洁的办法。

No Reply at the moment.
You need to Sign in before reply, if you don't have an account, please Sign up first.