1. ORM là gì?

ORM (Object-Relational Mapping) là kỹ thuật ánh xạ giữa các đối tượng trong chương trình và dữ liệu được lưu trữ trong cơ sở dữ liệu quan hệ.

Trong ứng dụng backend, dữ liệu thường được biểu diễn bằng struct hoặc object trong code, trong khi database lưu dữ liệu dưới dạng bảng, hàng và cột. ORM giúp kết nối hai cách biểu diễn này, cho phép developer thao tác với database thông qua các cấu trúc và API của ngôn ngữ lập trình thay vì phải tự viết SQL cho mọi thao tác.

Ví dụ, trong database có bảng users:

CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(255)
);

Trong Go, dữ liệu tương ứng có thể được biểu diễn bằng struct:

type User struct {
    ID    uint
    Name  string
    Email string
}

Nếu không sử dụng ORM, developer thường phải viết SQL, thực thi câu lệnh, đọc kết quả và ánh xạ từng dòng dữ liệu vào struct.

Khi sử dụng ORM như GORM, có thể truy vấn bằng:

var user User

err := db.First(&user, 1).Error
if err != nil {
    return err
}

GORM xây dựng và thực thi câu SQL tương ứng, sau đó đưa dữ liệu truy vấn được vào biến user.

2. ORM giải quyết vấn đề gì?

Nếu không có ORM, application phải tự xử lý nhiều công việc khi giao tiếp với database:

  • Viết câu SQL cho từng thao tác đọc, thêm, sửa và xóa dữ liệu.
  • Ánh xạ kết quả truy vấn sang struct hoặc object.
  • Xử lý kiểu dữ liệu, trường có thể NULL và giá trị mặc định.
  • Xây dựng các truy vấn có điều kiện, phân trang và sắp xếp.
  • Xử lý các mối quan hệ giữa những bảng.
  • Quản lý transaction và lỗi khi thao tác database.

Những công việc này không quá phức tạp khi ứng dụng nhỏ, nhưng có thể trở nên lặp lại và khó bảo trì khi hệ thống phát triển. ORM cung cấp một lớp trừu tượng để giảm phần công việc đó. Developer có thể tập trung hơn vào cấu trúc dữ liệu và nghiệp vụ, thay vì tự ánh xạ kết quả của mọi câu truy vấn.

3. ORM hoạt động như thế nào?

Một ORM thường thực hiện các công việc chính sau:

Mapping – Ánh xạ dữ liệu: ORM xác định struct hoặc class nào tương ứng với bảng nào, field nào tương ứng với column nào và quan hệ giữa các model được biểu diễn ra sao.

Query building – Xây dựng truy vấn: Khi developer gọi các phương thức như First, Where, Create hoặc Updates, ORM xây dựng câu SQL phù hợp với thao tác được yêu cầu.

Execution – Thực thi SQL: Câu SQL được gửi tới database thông qua driver hoặc lớp kết nối tương ứng.

Result mapping – Ánh xạ kết quả: Với truy vấn đọc dữ liệu, ORM chuyển các row trả về thành struct hoặc object để application sử dụng.

Relationship handling – Xử lý quan hệ: ORM cung cấp cách truy vấn hoặc lưu dữ liệu có liên quan giữa những model, chẳng hạn một user có nhiều order.

Điều quan trọng là ORM vẫn thực thi các thao tác trên database. Nó không khiến dữ liệu tồn tại tự động trong bộ nhớ của application và cũng không có nghĩa mọi thay đổi của object đều tự động được lưu vào database.

4. Model – Ánh xạ giữa struct và table

Trong GORM, model thường được định nghĩa bằng một Go struct. ORM dựa vào các quy ước mặc định hoặc tag để xác định cách ánh xạ dữ liệu.

Ví dụ:

package models

import "time"

type User struct {
    ID        uint      `gorm:"primaryKey"`
    Name      string
    Email     string    `gorm:"uniqueIndex"`
    Active    bool
    CreatedAt time.Time
    UpdatedAt time.Time
}

Với các quy ước mặc định của GORM, model User thường tương ứng với bảng users, còn các field như CreatedAt và UpdatedAt được ánh xạ sang những column có tên created_at và updated_at.

Một số quy ước quan trọng:

  • ID thường được dùng làm primary key mặc định.
  • Tên struct được chuyển thành tên bảng số nhiều theo quy ước của GORM.
  • Tên field được ánh xạ sang tên column dạng snake_case.
  • CreatedAt và UpdatedAt được tự động quản lý thời gian trong các thao tác mà GORM hỗ trợ.
  • gorm tag cho phép tùy chỉnh primary key, unique index, column name và những quy tắc khác.

Ví dụ sử dụng tag để tùy chỉnh tên column:

type User struct {
    ID        uint   `gorm:"primaryKey"`
    FullName  string `gorm:"column:full_name"`
}

Khi đó, field FullName được ánh xạ vào column full_name.

5. Relationship – Quan hệ giữa các model

ột trong những chức năng quan trọng của ORM là biểu diễn các mối quan hệ giữa bảng bằng field hoặc association trong model.

Bốn loại quan hệ thường gặp là:

One-to-One: Một bản ghi có quan hệ với một bản ghi khác, chẳng hạn user và profile.

One-to-Many: Một bản ghi có thể liên kết với nhiều bản ghi khác, chẳng hạn một user có nhiều order.

Many-to-One: Nhiều bản ghi cùng tham chiếu về một bản ghi, chẳng hạn nhiều order thuộc về một user.

Many-to-Many: Nhiều bản ghi ở bảng này có thể liên kết với nhiều bản ghi ở bảng khác, chẳng hạn users và roles thông qua bảng trung gian user_roles.

Ví dụ quan hệ một user có nhiều order:

type User struct {
    ID     uint
    Name   string
    Orders []Order
}

type Order struct {
    ID     uint
    UserID uint
    Status string
}

Trong ví dụ này, Orders biểu diễn mối quan hệ từ User tới nhiều Order, còn UserID là foreign key tham chiếu đến user tương ứng. ORM giúp truy vấn và lưu các model liên quan thuận tiện hơn. Tuy nhiên, quan hệ trong struct không thay thế constraint ở database.

6. ORM và Transaction

ORM thường cung cấp API để nhóm nhiều thao tác database trong cùng một transaction.

Ví dụ với GORM:

err := db.Transaction(func(tx *gorm.DB) error {
    if err := tx.Create(&order).Error; err != nil {
        return err
    }

    if err := tx.Create(&orderItem).Error; err != nil {
        return err
    }

    return nil
})

Nếu callback trả về nil, GORM commit transaction. Nếu callback trả về lỗi, GORM rollback transaction.

Trong ví dụ, order và orderItem được tạo trong cùng transaction, do đó không nên để một thao tác thành công và thao tác còn lại thất bại mà vẫn commit như một giao dịch hoàn chỉnh.

7. Tổng kết

ORM (Object-Relational Mapping) là lớp trừu tượng giúp ánh xạ model trong chương trình với bảng và dữ liệu trong database quan hệ. ORM cung cấp các API để thao tác CRUD, xây dựng query, xử lý quan hệ, quản lý transaction và hỗ trợ một số công việc về schema. GORM là một ORM phổ biến trong Go. Khi làm việc với GORM, những nội dung nên nắm vững trước tiên là model mapping, CRUD, query conditions, relationship, Preload, transaction và cách kiểm tra SQL được sinh ra.

Bài viết khác

Series 7 — Web Quality – Engineering :Testing – Unit Testing, Integration Testing, Web Performance – Core Web Vitals, Code Splitting, Lazy Loading

Series 7 — Web Quality – Engineering Bài 3: Testing – Unit Testing, Integration Testing và End-to-End Testing trong ứng dụng web 1. Vì sao ứng dụng web cần Testing? Trong quá trình phát triển Frontend, chúng ta thường kiểm tra ứng dụng bằng cách chạy website trên trình duyệt, nhấn vào các nút, nhập […]

Series 7 — Web Quality – Engineering : Authentication & Authorization

Series 7 — Web Quality – Engineering Bài 1: Authentication & Authorization – Xác thực và phân quyền trong ứng dụng web 1. Mở đầu Khi xây dựng một ứng dụng web có chức năng đăng nhập, chúng ta thường nghĩ rằng chỉ cần tạo biểu mẫu nhập email, mật khẩu và kiểm tra thông […]

Series 6 – Next.js Advanced – Production: Next.js Build & Deployment

Series 6 – Next.js Advanced & Production Bài 5: Next.js Build & Deployment – Từ mã nguồn đến môi trường Production 1. Mở đầu Trong quá trình phát triển một ứng dụng web, việc chạy thành công trên máy tính cá nhân chưa có nghĩa là ứng dụng đã sẵn sàng để đưa đến người […]

Series 6 – Next.js Advanced – Production: Next.js Internationalized Routing Next.js Static Export

Series 6 – Next.js Advanced & Production   Bài 3: Next.js Internationalized Routing – Tổ chức URL cho website đa ngôn ngữ 1. Mở đầu Sau khi xây dựng cơ chế dịch nội dung và định dạng theo locale, ứng dụng cần một cách để người dùng truy cập đúng phiên bản ngôn ngữ. Ví […]

Series 6 – Next.js Advanced & Production: Metadata & SEO, Internationalization (i18n) & Localization (l10n)

Series 6 – Next.js Advanced & Production Bài 1: Metadata & SEO trong Next.js – Tối ưu thông tin và khả năng hiển thị trên công cụ tìm kiếm 1. Mở đầu Khi xây dựng một website, việc hoàn thiện giao diện và chức năng mới chỉ là một phần của quá trình phát triển. […]

Series 5 – Next.js Fundamentals: CSR, SSR, SSG, ISR và cách lựa chọn

Series 5 – Next.js Fundamentals   Bài 7 : CSR, SSR, SSG và ISR – Các chiến lược Rendering trong Next.js 1. Mở đầu Khi xây dựng một website bằng React, chúng ta thường tập trung vào việc tạo component, quản lý state và hiển thị giao diện trên trình duyệt. Tuy nhiên, khi ứng dụng […]

Leave a Reply

Your email address will not be published. Required fields are marked *