繁体   English   中英

Ruby on Rails的ActiveRecord与DBMS脚本

[英]Ruby on Rails's ActiveRecord vs DBMS scripts

我正在学习Ruby on Rails,并遵循一些指南来了解该框架。

目前,我正在阅读有关ActiveRecord迁移的信息,在开发阶段特别是当您使用敏捷并且经常更改需求时,最好跟踪一下更改。

但是,我相信,如果您对数据库进行了微调,最好使用特定于供应商的脚本(MySQL,Postgres等)。

不用说,我并不是要使这篇文章基于观点,所以我的第一个问题是,您是否了解使用Rails构建应用程序的这种方法的优缺点( ActiveRecord迁移与DBMS特定脚本)。 我一直在互联网上搜索,但没有找到任何比较。

另外,我想知道是否存在任何风险,应该注意的事情,或者如果需要将这两种方法结合使用,可以避免这样做。

预先感谢您的评论/答案。 最好的祝福

TLDR:

您不想使用第三方工具来手动管理数据库。 尝试使您的数据库代码尽可能接近Rails的迁移。

故事

如果您正在执行CRUD应用程序,而在应用程序或数据库端都不需要任何复杂的逻辑,那么Rails迁移就很棒。 此功能使您可以将增量更改写入数据库并回滚它们,而不会中断生产中的应用程序。 假设您有一个“用户”表,并想添加一个字段,我们称之为“ second_email_address”。 您可以这样做:

class AddSecondEmailAddressToUsers < ActiveRecord::Migration[5.2]

  def self.up
    add_column :users, :second_email_address, :string
  end

  def self.down
    remove_column :users, :second_email_address, :string
  end

end

Rails会根据迁移文件名跟踪数据库模式的“版本”,并能准确告诉您您的位置,并使您可以回滚任何您改变主意的事情。 您可以添加或删除此列,而不会丢失表中的任何数据,除非存储在该列中的数据除外。 这对于基本用例非常好。

当您想使数据库更具个性时,事情会变得更加复杂。

以触​​发器为例,假设您有一个表“ shops”,具有has_many:users,并且您希望在shops表的整数列中跟踪用户计数。 Rails关于配置的约定范式要求您在用户模型中执行以下操作:

after_create :increase_shop_user_count
def increase_shop_user_count
  self.shop.user_count+=1
end

有了触发器,您就可以...

create or replace trigger increase_shop_user_count
  after insert on users
  for each row
  begin
    update shops set user_count = user_count + 1 where shops.id = NEW.shop_id
  end;

使用触发器的性能提升是惊人的。 但是,在大多数现实生活中,您都不关心此事,并且乐于权衡几毫秒的延迟,以方便将其全部以Rails方式包含在Rails应用程序中。 但是,我保证当您有数千家商店,每个商店有数十万用户,并且应用程序中有许多其他类似甚至更复杂的功能时,您会改变主意。 没错,申请成功意味着您必须动手(并学会忽略别人的卑鄙评论,这些人会通知您您将在熔岩中煮沸直到永恒结束,因为他们不尊重“铁路方式”,nts nts )。

在我的应用程序中,触发器,过程,计划的事件中有大约2万行SQL,这些行从Rails应用程序代码库中移出并移至数据库层。 当然,它从多个方面(编写SQL,迁移,测试...)都增加了一定程度的复杂性,但是到月底,公司每月要少支付2万美元的EC2账单。

例如,如果您想直接使用phpmyadmin进行此类操作,则必须手动执行每个操作,但是随着应用程序复杂性的提高,您将不知所措,这转化为支持午夜和停机时间的呼叫。 幸运的是,您仍然可以将Rails迁移与触发器/存储过程等配合使用。

您可以按照上面的示例在单独的迁移文件中编写SQL。 您可以使用例如hair_trigger之类的gem在模型内定义触发器,并将其导出到schema.rb或structure.sql(尽管这并不涵盖,并且过程/函数和事件似乎没有等效项)。 您还可以将模式切换到SQL,并且所有代码都会得到一个庞大的SQL文件,这太麻烦了。

但是,由于这不再是要定义表和列,而是更像是应用程序功能,因此您还可以在app /目录中包含一个sql /目录,并在其中对SQL代码进行分组,例如:

# app/sql/mysql/triggers/increase_shop_user_count.sql
    create or replace trigger increase_shop_user_count
      after insert on users
      for each row
      begin
        update shops set user_count = user_count + 1 where shops.id = NEW.shop_id
      end;

然后在Rails迁移文件中执行以下操作:

class IncreaseShopUserCountTrigger < ActiveRecord::Migration[5.2]
  def self.up
    execute File.read( Rails.root.join("app","sql","mysql","triggers","increase_shop_user_count.sql"))
  end
  def self.down
    execute "drop trigger increase_shop_user_count"
  end
end

当然,当您有数百个这样的婴儿时,您希望有一个发现机制,这样您就不必执行File.read ...每个触发器/过程都像蛮子一样。

底线:一点都不差! 因此,Rails迁移使您能够对数据库进行增量更改,并使它们保持井井有条,版本化且易于管理!

但是测试呢?

好吧,您会再一次脱离Rails约定的铺砌好的道路。 您可以像这样测试模型的行为:

it "changes shop user count after creation" do
  s = create :shop
  u = create :user, shop: s
  expect(s.reload.user_count).to eq 1 # because our database-side magic changed the count!
end

以后的编辑:上面的用户计数器示例当然可以使用AR的计数器缓存功能进行重构,但希望可以证明这一点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM