Xem source code tại đây

Trong phần trước mình đã đưa ra ví dụ giúp hiểu được một cách bao quát những lệnh truy vấn căn bản, định nghĩa model go-pg. Trước khi đi vào bổ sung, cải tiến ví dụ đó, trong bài này mình sẽ nói rõ về định nghĩa model và quan hệ giữa các model để giúp bạn hiểu bản chất.

1. Giới thiệu

Go-pg sử dụng công nghệ ORM (tức Object-relation mapping) giúp ánh xạ bảng cơ sở dữ liệu vào trong struct

Điều đấy có nghĩa là với mỗi struct trong golang có thể dùng làm đại diện để truy vấn đến bảng trong postgresql và trả ra đối tượng struct với giá trị tương ứng.

2. Cách định nghĩa model, tên struct, tên cột hợp lý

Go-pg có thể tự động nhận biết tên trường, tên struct, kiểu biến để khởi tạo bảng trong postgresql. Ví dụ:

  • nếu tên struct là Genre sẽ mặc định có tên bảng là genres
  • tên trường là FullName sẽ có tên cột là full_name
  • Kiểu string sẽ tương ứng với kiểu text trong database, int thành bigint,...
  • Trường có tên là Id tự động được thêm vào primary key.

Nếu không muốn để mặc định thì có thể ghi đè bằng những tags như: pg:"type:kiểu",pg:"tên cột",... hay thêm các ràng buộc cho cột như pg:,unique

Có thể tham khảo thêm tại đây

Ví dụ về định nghĩa model:

type User struct{
    tableName struct{} `pg:"auth.users"`    //Tên schema auth, bảng users

    Id int `pg:"type:serial,pk"`    //Cột id đặt là primary key, với kiểu là serial

    FirstName string    //String sẽ tương ứng với kiểu text trong database, tên cột là first_name

    LastName string     //tên cột là last_name

    Email string    `pg:",unique"`  //Email điều kiện là không được trùng lặp

    Password string
}
  • Trường Id trong model User trên thay vì để kiểu mặc định là bigint, ta thay bằng serial. Và đuôi ,pk không thực sự cần thiết vì id sẽ mặc định là primary key
  • Nếu bỏ dòng tableName thì tên table sẽ mặc định là users. Nếu muốn ghi đè tên mặc định hoặc tạo bảng trong một schema thì đây là điều bắt buộc

3. Định nghĩa model quan hệ 1 - 1

Giả sử ta có 2 bảng User với Profile, với mỗi User chỉ có một Profile và ngược lại. Đây là quan hệ một một. Ta có thể định nghĩa model như sau

type User struct{
    tableName struct{} `pg:"auth.users"`

    Id int `pg:"serial"`

    Name string

    Profile *Profile `pg:"rel:belongs-to"`  //Profile thuộc về User
}
type Profile struct{
    tableName struct{} `pg:"auth.profile"`

    Id     int    `pg:"type:serial"`

    Avatar string

    UserId int  `pg:",unique"` //Id của User mà Profile thuộc về, đây là trường cần thiết để go-pg hiểu được quan hệ
}

Lưu ý: - Việc đặt unique ở UserId trong Profile không bắt buộc, chỉ nhằm để không có UserId nào giống nhau, như vậy sẽ đảm bảo quan hệ thực sự là 1 - 1 hơn. Nếu không có unique ta có thể có 2 Profile với cùng một UserId, như vậy sẽ mất đi bản chất quan hệ 1 - 1.

4. Định nghĩa model quan hệ 1 - nhiều

Một user có thể có nhiều bài post. Ta có thể định nghĩa model thể hiện quan hệ này như sau:

type User struct{
    tableName struct{}  `pg:"auth.users"`

    Name string

    //Với mỗi user có thể lấy ra nhiều bài post của chính user đó
    Posts []Post  `pg:"rel:has-many,join_fk:user_id"`
}

Lưu ý: chỉ nên dùng join_fk khi ta muốn sử dụng foreign key với tên khác mặc định. Trong ví dụ trên không thực sự cần thiết

type Post struct{
    tableName struct{} `pg:"blog.post"`

    Id int `pg:"type:serial" `

    Content string `pg:",notnull"`

    Title string    `pg:",notnull"`

    //Đây là trường cần thiết để go-pg hiểu được quan hệ, nếu thay đổi cần thay đổi giá trị join_fk ở User trên
    UserId int `pg:"type:integer"`

    User *User `pg:"rel:has-one"`   //Mỗi post có thể lấy 1 user
}

5. Định nghĩa model quan hệ nhiều - nhiều

Một User có nhiều Role (như admin, teacher,...) và ngược lại một Role cũng có thể có nhiều User. Đây là quan hệ nhiều - nhiều. Trong quan hệ nhiều - nhiều, cách thường thấy là tạo bảng thứ 3 chứa hai id của 2 bảng còn lại. Ta có thể định nghĩa model thể hiện quan hệ này như sau:

type User struct{
    tableName struct{} `pg:"auth.users"`

    Id int `pg:"type:serial"`

    Name string

    //Quan hệ nhiều nhiều tham chiếu đến bảng auth.user_role để lấy giá trị
    Roles []Role `pg:"many2many:auth.user_role"`
}
type Role struct{
    tableName struct{} `pg:"auth.role"`

    Id int    `pg:"type:serial"`

    Name string
}
//Bảng thứ 3 tên auth.user_role thể hiện quan hệ giữa 2 bảng trên
type UserRole struct{

    tableName struct{} `pg:"auth.user_role"`

    UserId int  // Id của User

    RoleId int  // Id của Role
}

Để ORM hiểu được bảng user_role là bảng thể hiện quan hệ nhiều - nhiều của 2 bảng user và role. Ta cần dùng hàm RegisterTable như sau:

    orm.RegisterTable((*UserRole)(nil))

Lưu ý: ta cần chạy hàm này trước khi các model User và Role được sử dụng đến, cách tốt nhất là chạy ngay từ đầu khi mới bắt đầu kết nối database.

Kết

Việc định nghĩa struct model golang cũng chính là đang tạo bảng trong postgresql. Đây là bước cần thiết để ta có thể thao tác với cơ sở dữ liệu sau này. Trong phần sau mình sẽ đi vào ví dụ thực tế, chi tiết hơn cùng với các lệnh truy vấn JOIN bảng.