Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

ActiveRecord กับ EctoPart One

ข้อมูลเป็นส่วนสำคัญของแอปพลิเคชันซอฟต์แวร์ส่วนใหญ่ การทำแผนที่และการสืบค้นข้อมูลจากฐานข้อมูลเป็นงานที่เกิดซ้ำในชีวิตของนักพัฒนา ด้วยเหตุนี้ จึงเป็นสิ่งสำคัญที่จะต้องเข้าใจกระบวนการและสามารถใช้ abstractions ที่ทำให้งานง่ายขึ้นได้

ในโพสต์นี้ คุณจะพบการเปรียบเทียบระหว่าง ActiveRecord (Ruby) และ Ecto (Elixir) ซึ่งเป็นชุดแรกจากสองชุดข้อมูล เราจะมาดูกันว่าเครื่องมือทั้งสองช่วยให้นักพัฒนาย้ายข้อมูลและจับคู่สคีมาฐานข้อมูลได้อย่างไร

เราจะเปรียบเทียบแอปเปิ้ลกับส้ม (ต้นฉบับ) แบตเกิร์ลที่ไม่ต้องพูดอะไรเลย กับแบทแมน โดยระบุอย่างชัดเจนว่า 'ฉันคือแบทแมน' โดยนัย ข้อตกลงเกี่ยวกับการกำหนดค่า เทียบกับความตั้งใจที่ชัดเจน รอบที่หนึ่ง. สู้!

ActiveRecord

เป็นเวลากว่า 10 ปีนับตั้งแต่เปิดตัว คุณคงเคยได้ยินเกี่ยวกับ ActiveRecord ซึ่งเป็น ORM ที่มีชื่อเสียงซึ่งจัดส่งโดยค่าเริ่มต้นด้วยโปรเจ็กต์ Ruby on Rails

ActiveRecord คือ M ใน MVC - โมเดล - ซึ่งเป็นเลเยอร์ของระบบที่รับผิดชอบในการแสดงข้อมูลทางธุรกิจและตรรกะ ActiveRecord อำนวยความสะดวกในการสร้างและใช้งานอ็อบเจ็กต์ธุรกิจที่ข้อมูลต้องการการจัดเก็บข้อมูลแบบถาวรในฐานข้อมูล เป็นการนำรูปแบบ ActiveRecord ไปใช้งาน ซึ่งเป็นคำอธิบายของระบบ Object Relational Mapping

แม้ว่าส่วนใหญ่จะรู้จักกับ Rails แต่ ActiveRecord ก็สามารถใช้เป็นเครื่องมือแบบสแตนด์อโลนได้ โดยจะฝังอยู่ในโปรเจ็กต์อื่นๆ

Ecto

เมื่อเปรียบเทียบกับ ActiveRecord แล้ว Ecto เป็นเครื่องมือที่ค่อนข้างใหม่ (และในขณะนี้ยังไม่มีชื่อเสียง) เขียนด้วย Elixir และรวมอยู่ในโปรเจ็กต์ Phoenix โดยค่าเริ่มต้น

Ecto ไม่ใช่ ORM ต่างจาก ActiveRecord แต่เป็นไลบรารีที่ช่วยให้การใช้ Elixir เขียนข้อความค้นหาและโต้ตอบกับฐานข้อมูลได้

Ecto เป็นภาษาเฉพาะโดเมนสำหรับเขียนข้อความค้นหาและโต้ตอบกับฐานข้อมูลใน Elixir

จากการออกแบบ Ecto เป็นเครื่องมือแบบสแตนด์อโลน ซึ่งใช้ในโปรเจ็กต์ Elixir ต่างๆ และไม่ได้เชื่อมต่อกับเฟรมเวิร์กใดๆ

คุณเปรียบเทียบแอปเปิ้ลกับส้มไม่ใช่หรือ

ใช่! แม้ว่า ActiveRecord และ Ecto จะมีความหมายแตกต่างกัน แต่คุณลักษณะทั่วไป เช่น การโยกย้ายฐานข้อมูล การแมปฐานข้อมูล การสืบค้น และการตรวจสอบ ได้รับการสนับสนุนโดยทั้ง ActiveRecord และ Ecto และเราสามารถบรรลุผลลัพธ์เดียวกันได้โดยใช้เครื่องมือทั้งสอง สำหรับผู้ที่สนใจ Elixir ที่มีพื้นฐานมาจาก Ruby เราคิดว่านี่น่าจะเป็นการเปรียบเทียบที่น่าสนใจ

ระบบใบแจ้งหนี้

ตลอดช่วงที่เหลือของโพสต์ ระบบจะใช้ระบบใบแจ้งหนี้สมมติเพื่อสาธิต ลองนึกภาพว่าเรามีร้านขายชุดให้เหล่าซุปเปอร์ฮีโร่ เพื่อให้ง่ายขึ้น เราจะมีเพียงสองตารางสำหรับระบบใบแจ้งหนี้:ผู้ใช้ และ ใบแจ้งหนี้ .

ด้านล่างนี้คือโครงสร้างของตารางเหล่านั้น พร้อมด้วยฟิลด์และประเภท:

ผู้ใช้

ฟิลด์ พิมพ์
ชื่อเต็ม สตริง
อีเมล สตริง
created_at (ActiveRecord) / inserted_at (Ecto) วันที่และเวลา
updated_at วันที่และเวลา

ใบแจ้งหนี้

ฟิลด์ พิมพ์
user_id จำนวนเต็ม
payment_method สตริง
paid_at วันที่และเวลา
created_at (ActiveRecord) / inserted_at (Ecto) วันที่และเวลา
updated_at วันที่และเวลา

ตารางผู้ใช้มีสี่ช่อง:full_name , อีเมล , updated_at และช่องที่สี่ขึ้นอยู่กับเครื่องมือที่ใช้ ActiveRecord สร้าง created_at ฟิลด์ในขณะที่ Ecto สร้าง inserted_at เพื่อแสดงการประทับเวลาของช่วงเวลาที่บันทึกครั้งแรกในฐานข้อมูล

ตารางที่ 2 ชื่อ ใบแจ้งหนี้ . มีห้าช่อง:user_id , payment_method , paid_at , updated_at และคล้ายกับตารางผู้ใช้ created_at หรือ inserted_at ขึ้นอยู่กับเครื่องมือที่ใช้

ตารางผู้ใช้และใบแจ้งหนี้มีการเชื่อมโยงดังต่อไปนี้:

  • ผู้ใช้รายหนึ่งมีใบแจ้งหนี้จำนวนมาก
  • ใบแจ้งหนี้เป็นของผู้ใช้

การย้ายถิ่น

การย้ายข้อมูลช่วยให้นักพัฒนาสามารถพัฒนาสคีมาฐานข้อมูลของตนได้อย่างง่ายดายเมื่อเวลาผ่านไป โดยใช้กระบวนการวนซ้ำ ทั้ง ActiveRecord และ Ecto ช่วยให้นักพัฒนาสามารถโยกย้ายสคีมาฐานข้อมูลโดยใช้ภาษาระดับสูง (Ruby และ Elixir ตามลำดับ) แทนที่จะจัดการกับ SQL โดยตรง

มาดูกันว่าการย้ายข้อมูลทำงานอย่างไรใน ActiveRecord และ Ecto โดยใช้พวกมันเพื่อสร้างตารางผู้ใช้และใบแจ้งหนี้

ActiveRecord:การสร้างตารางผู้ใช้

การย้ายถิ่น

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :full_name, null: false
      t.string :email, index: {unique: true}, null: false
      t.timestamps
    end
  end
end

การโยกย้าย ActiveRecord ช่วยให้สามารถสร้างตารางโดยใช้ create_table กระบวนการ. แม้ว่า created_at และ updated_at ไม่ได้กำหนดฟิลด์ในไฟล์การโยกย้าย การใช้ t.timestamps ทริกเกอร์ ActiveRecord เพื่อสร้างทั้งสองอย่าง

สร้างโครงสร้างตาราง

หลังจากรัน CreateUsers การย้ายข้อมูล ตารางที่สร้างขึ้นจะมีโครงสร้างดังนี้:

   Column   |            Type             | Nullable |              Default
------------+-----------------------------+----------+-----------------------------------
 id         | bigint                      | not null | nextval('users_id_seq'::regclass)
 full_name  | character varying           | not null |
 email      | character varying           | not null |
 created_at | timestamp without time zone | not null |
 updated_at | timestamp without time zone | not null |
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
    "index_users_on_email" UNIQUE, btree (email)

การย้ายข้อมูลยังรับผิดชอบในการสร้างดัชนีเฉพาะสำหรับฟิลด์อีเมล ตัวเลือก index: {unique: true} ถูกส่งไปยังข้อกำหนดฟิลด์อีเมล นี่คือสาเหตุที่ตารางแสดงรายการ "index_users_on_email" UNIQUE, btree (email) ดัชนีเป็นส่วนหนึ่งของโครงสร้าง

Ecto:การสร้างตารางผู้ใช้

การย้ายถิ่น

defmodule Financex.Repo.Migrations.CreateUsers do
  use Ecto.Migration
 
  def change do
    create table(:users) do
      add :full_name, :string, null: false
      add :email, :string, null: false
      timestamps()
    end
 
    create index(:users, [:email], unique: true)
  end
end

การโยกย้าย Ecto รวมฟังก์ชัน create() และ table() เพื่อสร้างตารางผู้ใช้ ไฟล์การโยกย้าย Ecto ค่อนข้างคล้ายกับ ActiveRecord ที่เทียบเท่า ใน ActiveRecord ฟิลด์การประทับเวลา (created_at และ updated_at ) ถูกสร้างขึ้นโดย t.timestamps ในขณะที่อยู่ใน Ecto ฟิลด์การประทับเวลา (inserted_at และ updated_at ) ถูกสร้างขึ้นโดย timestamps() ฟังก์ชัน

เครื่องมือทั้งสองมีความแตกต่างกันเล็กน้อยเกี่ยวกับวิธีการสร้างดัชนี ใน ActiveRecord ดัชนีถูกกำหนดเป็นตัวเลือกสำหรับฟิลด์ที่กำลังสร้าง Ecto ใช้การรวมฟังก์ชัน create() และ index() เพื่อให้บรรลุเป้าหมายนั้น สอดคล้องกับวิธีการใช้ชุดค่าผสมเพื่อสร้างตารางเอง

สร้างโครงสร้างตาราง

   Column    |            Type             | Nullable |              Default
-------------+-----------------------------+----------+-----------------------------------
 id          | bigint                      | not null | nextval('users_id_seq'::regclass)
 full_name   | character varying(255)      | not null |
 email       | character varying(255)      | not null |
 inserted_at | timestamp without time zone | not null |
 updated_at  | timestamp without time zone | not null |
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
    "users_email_index" UNIQUE, btree (email)

ตารางที่สร้างจากการรัน Financex.Repo.Migrations.CreateUsers การย้ายข้อมูลมีโครงสร้างเหมือนกันกับตารางที่สร้างโดยใช้ ActiveRecord

ActiveRecord:การสร้าง invoices ตาราง

การย้ายถิ่น

class CreateInvoices < ActiveRecord::Migration[5.2]
  def change
    create_table :invoices do |t|
      t.references :user
      t.string :payment_method
      t.datetime :paid_at
      t.timestamps
    end
  end
end

การย้ายข้อมูลนี้รวมถึง t.references วิธีที่ไม่มีอยู่ในวิธีก่อนหน้า ใช้เพื่อสร้างการอ้างอิงไปยังตารางผู้ใช้ ตามที่อธิบายไว้ก่อนหน้านี้ ผู้ใช้มีใบแจ้งหนี้จำนวนมากและใบแจ้งหนี้เป็นของผู้ใช้ t.references เมธอดสร้าง user_id ในตารางใบแจ้งหนี้เพื่อใช้อ้างอิง

สร้างโครงสร้างตาราง

     Column     |            Type             | Nullable |               Default
----------------+-----------------------------+----------+--------------------------------------
 id             | bigint                      | not null | nextval('invoices_id_seq'::regclass)
 user_id        | bigint                      |          |
 payment_method | character varying           |          |
 paid_at        | timestamp without time zone |          |
 created_at     | timestamp without time zone | not null |
 updated_at     | timestamp without time zone | not null |
Indexes:
    "invoices_pkey" PRIMARY KEY, btree (id)
    "index_invoices_on_user_id" btree (user_id)

ตารางที่สร้างขึ้นจะมีรูปแบบเดียวกับตารางที่สร้างไว้ก่อนหน้านี้ ข้อแตกต่างเพียงอย่างเดียวคือดัชนีพิเศษ (index_invoices_on_user_id ) ซึ่ง ActiveRecord จะเพิ่มโดยอัตโนมัติเมื่อ t.references ใช้วิธีการ

Ecto:การสร้าง invoices ตาราง

การย้ายถิ่น

defmodule Financex.Repo.Migrations.CreateInvoices do
  use Ecto.Migration
 
  def change do
    create table(:invoices) do
      add :user_id, references(:users)
      add :payment_method, :string
      add :paid_at, :utc_datetime
      timestamps()
    end
 
    create index(:invoices, [:user_id])
  end
end

Ecto ยังสนับสนุนการสร้างการอ้างอิงฐานข้อมูล โดยใช้ references() การทำงาน. ซึ่งแตกต่างจาก ActiveRecord ซึ่งอนุมานชื่อคอลัมน์ Ecto ต้องการให้นักพัฒนากำหนด user_id อย่างชัดเจน ชื่อคอลัมน์ references() ฟังก์ชันยังต้องให้นักพัฒนากำหนดตารางที่อ้างอิงถึงอย่างชัดเจน ซึ่งในตัวอย่างนี้คือตารางผู้ใช้

สร้างโครงสร้างตาราง

     Column     |            Type             | Nullable |               Default
----------------+-----------------------------+----------+--------------------------------------
 id             | bigint                      | not null | nextval('invoices_id_seq'::regclass)
 user_id        | bigint                      |          |
 payment_method | character varying(255)      |          |
 paid_at        | timestamp without time zone |          |
 inserted_at    | timestamp without time zone | not null |
 updated_at     | timestamp without time zone | not null |

Indexes:
    "invoices_pkey" PRIMARY KEY, btree (id)
    "invoices_user_id_index" btree (user_id)
Foreign-key constraints:
    "invoices_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)

การโยกย้ายทั้งสองยังค่อนข้างคล้ายกัน เมื่อพูดถึง references มีการจัดการคุณลักษณะ มีความแตกต่างเล็กน้อย:

  1. Ecto สร้างข้อ จำกัด ของคีย์ต่างประเทศให้กับ user_id ฟิลด์ ("invoices_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ) ซึ่งรักษาความสมบูรณ์ของการอ้างอิงระหว่างผู้ใช้และตารางใบแจ้งหนี้

  2. ActiveRecord สร้างดัชนีโดยอัตโนมัติสำหรับ user_id คอลัมน์. Ecto ต้องการให้นักพัฒนามีความชัดเจนในเรื่องนี้ นี่คือสาเหตุที่การย้ายข้อมูลมี create index(:invoices, [:user_id]) คำชี้แจง

ActiveRecord:การแมปข้อมูลและการเชื่อมโยง

ActiveRecord เป็นที่รู้จักสำหรับคำขวัญ "การประชุมผ่านการกำหนดค่า" มันอนุมานชื่อตารางฐานข้อมูลโดยใช้ชื่อคลาสโมเดล โดยดีฟอลต์ คลาสชื่อ User โดยค่าเริ่มต้น จะใช้ users ตารางเป็นแหล่งที่มา ActiveRecord ยังจับคู่คอลัมน์ทั้งหมดของตารางเป็นแอตทริบิวต์ของอินสแตนซ์ นักพัฒนาจำเป็นต้องกำหนดความสัมพันธ์ระหว่างตารางเท่านั้น ActiveRecord ยังใช้สิ่งเหล่านี้เพื่อสรุปคลาสและตารางที่เกี่ยวข้อง

ดูวิธีการจับคู่ผู้ใช้และตารางใบแจ้งหนี้โดยใช้ ActiveRecord:

ผู้ใช้

class User < ApplicationRecord
  has_many :invoices
end

ใบแจ้งหนี้

class Invoice < ApplicationRecord
  belongs_to :user
end

Ecto:การแมปข้อมูลและการเชื่อมโยง

ในทางกลับกัน Ecto ต้องการให้นักพัฒนามีความชัดเจนเกี่ยวกับแหล่งข้อมูลและฟิลด์ต่างๆ แม้ว่า Ecto จะมี has_many . ที่คล้ายกัน และ belongs_to นอกจากนี้ ยังต้องการให้นักพัฒนามีความชัดเจนเกี่ยวกับตารางที่เกี่ยวข้องและโมดูลสคีมาที่ใช้ในการจัดการกับสคีมาของตารางนั้นด้วย

นี่คือวิธีที่ Ecto จับคู่ผู้ใช้และตารางใบแจ้งหนี้:

ผู้ใช้

defmodule Financex.Accounts.User do
  use Ecto.Schema
 
  schema "users" do
    field :full_name, :string
    field :email, :string
    has_many :invoices, Financex.Accounts.Invoice
    timestamps()
  end
end

ใบแจ้งหนี้

defmodule Financex.Accounts.Invoice do
  use Ecto.Schema
 
  schema "invoices" do
    field :payment_method, :string
    field :paid_at, :utc_datetime
    belongs_to :user, Financex.Accounts.User
    timestamps()
  end
end

สรุป

ในโพสต์นี้ เราเปรียบเทียบแอปเปิ้ลกับส้มโดยไม่กะพริบตา เราเปรียบเทียบวิธีที่ ActiveRecord และ Ecto จัดการกับการย้ายฐานข้อมูลและการทำแผนที่ การต่อสู้ของแบตเกิร์ลดั้งเดิมโดยปริยายกับแบทแมน 'I'm Batman' ที่ชัดเจน

ขอบคุณ "การประชุมผ่านการกำหนดค่า" การใช้ ActiveRecord มักจะเกี่ยวข้องกับการเขียนน้อยลง Ecto ไปในทิศทางตรงกันข้าม ทำให้นักพัฒนาต้องมีความชัดเจนมากขึ้นเกี่ยวกับเจตนาของพวกเขา นอกเหนือจาก "โค้ดที่น้อยกว่า" โดยทั่วไปแล้ว ActiveRecord มีค่าเริ่มต้นที่เหมาะสมซึ่งช่วยนักพัฒนาไม่ต้องตัดสินใจในทุกสิ่งและยังต้องเข้าใจการกำหนดค่าพื้นฐานทั้งหมด สำหรับผู้เริ่มต้น ActiveRecord เป็นโซลูชันที่เหมาะสมกว่า เพราะมันทำให้การตัดสินใจ "ดีพอ" เป็นค่าเริ่มต้น ตราบใดที่คุณปฏิบัติตามมาตรฐานอย่างเคร่งครัด

ลักษณะที่ชัดเจนของ Ecto ช่วยให้อ่านและทำความเข้าใจพฤติกรรมของโค้ดได้ง่ายขึ้น แต่ยังต้องการให้นักพัฒนาเข้าใจเพิ่มเติมเกี่ยวกับคุณสมบัติของฐานข้อมูลและคุณลักษณะที่พร้อมใช้งาน สิ่งที่ทำให้ Ecto ดูยุ่งยากในแวบแรกคือหนึ่งในข้อดีของมัน จากประสบการณ์ส่วนตัวของฉันทั้งในโลกของ ActiveRecord และ Ecto ความชัดเจนของ Ecto จะลบเอฟเฟกต์ "เบื้องหลัง" และความไม่แน่นอนซึ่งพบได้ทั่วไปในโปรเจ็กต์ที่มี ActiveRecord สิ่งที่นักพัฒนาซอฟต์แวร์อ่านในโค้ดคือสิ่งที่เกิดขึ้นในแอปพลิเคชันและไม่มีพฤติกรรมโดยนัย

ในบล็อกที่สองในอีกไม่กี่สัปดาห์ ในชุดสองส่วน "ActiveRecord vs Ecto" เราจะอธิบายวิธีการทำงานของคิวรีและการตรวจสอบความถูกต้องทั้งใน ActiveRecord และ Ecto

เราชอบที่จะรู้ว่าคุณคิดอย่างไรกับบทความนี้ เรามองหาหัวข้อใหม่ๆ อยู่เสมอ ดังนั้นหากคุณมีหัวข้อที่ต้องการเรียนรู้เพิ่มเติม โปรดอย่าลังเลที่จะแจ้งให้เราทราบที่ @AppSignal!