You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby结合ActiveRecord如何同时连接多个SQLite数据库文件?

实现方案

核心逻辑是用独立抽象ActiveRecord基类隔离不同版本的数据库连接,同时把通用模型、序列化器逻辑封装为可复用模块,动态注入到对应版本的类中,全程不需要重复写业务代码、不需要预配置database.yml。

步骤1:封装通用业务逻辑

把所有模型、序列化器的通用逻辑写成模块,不要硬编码继承关系:

# 通用模型逻辑,所有版本共用
module Models
  module User
    extend ActiveSupport::Concern
    included do
      # 原有模型的关联、验证、scope、业务方法都放在这里
      has_many :orders
      scope :valid, -> { where(deleted_at: nil) }
      def full_name
        "#{first_name} #{last_name}"
      end
    end
  end

  module Order
    extend ActiveSupport::Concern
    included do
      belongs_to :user
      enum status: [:pending, :paid, :canceled]
    end
  end
  # 剩余73个模型同理封装为模块
end

# 通用序列化器逻辑,所有版本共用
module Serializers
  module UserSerializer
    extend ActiveSupport::Concern
    included do
      attributes :id, :full_name, :created_at
    end
  end
  # 其他序列化器同理封装
end

步骤2:实现版本数据库管理类

通过这个类动态生成每个版本的连接、模型、序列化器,自动缓存实例避免重复建连:

class VersionDB
  # 已初始化的版本实例缓存
  @instance_cache = {}

  attr_reader :version, :models, :serializers

  # 对外入口:传入版本号获取对应数据库实例,首次调用自动初始化
  def self.get(version)
    @instance_cache[version] ||= new(version)
  end

  # 清理过期版本,释放连接
  def self.cleanup(expired_versions)
    expired_versions.each do |v|
      instance = @instance_cache.delete(v)
      instance&.connection_handler.remove_connection(instance.instance_variable_get(:@base_class))
    end
  end

  private_class_method :new

  def initialize(version)
    @version = version
    setup_base_connection
    setup_models
    setup_serializers
  end

  def setup_base_connection
    # 每个版本对应独立的抽象Base类,连接完全隔离
    @base_class = Class.new(ActiveRecord::Base) do
      self.abstract_class = true
    end
    # 动态建立连接,不需要提前配置database.yml
    @base_class.establish_connection(
      adapter: 'sqlite3',
      database: "tmp/#{version}.sqlite",
      pool: 5,
      timeout: 5000
    )
  end

  def setup_models
    @models = Module.new
    Models.constants.each do |model_const|
      model_logic = Models.const_get(model_const)
      # 动态生成对应版本的模型类,继承版本Base、注入通用逻辑
      model_class = Class.new(@base_class) do
        include model_logic
        self.table_name = model_const.to_s.underscore.pluralize
      end
      @models.const_set(model_const, model_class)
    end
  end

  def setup_serializers
    @serializers = Module.new
    Serializers.constants.each do |ser_const|
      ser_logic = Serializers.const_get(ser_const)
      ser_class = Class.new(ActiveModel::Serializer) do
        include ser_logic
      end
      @serializers.const_set(ser_const, ser_class)
    end
  end
end

步骤3:使用方式

完全符合你预期的调用效果,各版本连接完全隔离:

# 按需获取任意版本的数据库实例
db_202103 = VersionDB.get(20210301)
db_202104 = VersionDB.get(20210401)
db_202105 = VersionDB.get(20210501)

# 独立查询,不会串库
users_202103 = db_202103.models::User.valid.all
order_202104 = db_202104.models::Order.find_by(status: :paid)
new_user = db_202105.models::User.create(first_name: '张', last_name: '三')

# 使用对应版本的序列化器
user_json = db_202103.serializers::UserSerializer.new(users_202103.first).serializable_hash

# 每月清理过期版本,不需要修改任何业务代码
VersionDB.cleanup([20210301])

方案优势

  • 完全符合DRY原则,所有业务逻辑只写一份,不需要维护多份重复的模型、序列化器文件
  • 不需要提前在database.yml配置所有版本,连接完全动态生成
  • 月度版本更新不需要修改代码,新版本直接调用VersionDB.get(新版本号)即可自动生成所有依赖
  • 连接完全隔离,不同版本的查询、写入操作不会互相影响
  • 过期版本清理一键完成,不需要删除静态文件

内容的提问来源于stack exchange,提问作者yenadar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 23:36:02