ORM là tên viết tắt của cụm từ “ Object Relational Mapping” đây là tên gọi chỉ việc ánh xạ các record dữ liệu trong hệ quản trị cơ sở dữ liệu sang các cấu trúc dữ liệu (đối tượng) trong các ngôn ngữ lập trình
GORM là một thư viện ORM dành riêng cho Golang. Trong bài viết này chúng ta sẽ cùng tìm hiểu về thư viện tuyệt vời này
Chi tiết về thư viện GORM, các bạn có thể tham khảo tại link sau : https://gorm.io/docs/index.html
Tạo cơ sở dữ liệu mysql
Trong bài viết này chúng ta sẽ sử dụng GORM để kết nối với hệ CSDL mysql, vì vậy chúng ta cần cài đặt mysql trước
Chúng ta có thể cài trực tiếp hệ cơ sở dữ liệu mysql trên máy hoặc sử dụng docker để cài đặt. Ở bài viết này thì mình sẽ cài mysql sử dụng docker
Chúng ta có thể cài mysql thông qua docker compose hoặc docker run
1.Sử dụng docker compose
Tạo file docker-compose.yml
volumes
data_db
services
mysql
image mysql latest
volumes
data_db:/var/lib/mysql
ports
3306:3306
environment
MYSQL_PASSWORD123
MYSQL_ROOT_PASSWORD123
MYSQL_DATABASE db
Sau đó chạy file docker compose để khởi tạo container
docker-compose up -d
2.Sử dụng docker run
docker run -d --name db_mysql -p 3306:3306 \
-e MYSQL_PASSWORD=123 \
-e MYSQL_ROOT_PASSWORD=123 \
-e MYSQL_DATABASE=db \
mysql:latest
Trường hợp máy bạn sử dụng là laptop M1 không thể pull image mysql thì xem hướng dẫn tại đây : https://stackoverflow.com/questions/65456814/docker-apple-silicon-m1-preview-mysql-no-matching-manifest-for-linux-arm64-v8
Kết nối với CSDL sử dụng DBeaver
DBeaver là ứng dụng UI giúp chúng ta kết nối và thao tác với các hệ CSDL : Postgresql, mysql, DB2, Oracle, ...
Trường hợp bị lỗi public key hãy tham khảo link sau : https://community.atlassian.com/t5/Confluence-questions/MySQL-Public-Key-Retrieval-is-not-allowed/qaq-p/778956
Nếu thành công, chúng ta sẽ có giao diện về mysql như sau
Bây giờ chúng ta sẽ tạo bảng student và insert một số bản ghi cho bảng
CREATE TABLE db.student (
id varchar(10) NOT NULL,
full_name varchar(50),
email varchar(50),
phone varchar(50),
card_id varchar(50),
PRIMARY KEY (id)
)
INSERT INTO db.student(id,full_name,email,phone,card_id) VALUES
('1','anna','anna@gmail.com','0987654321','100'),
('2','bob','bob@gmail.com','0344005934','101'),
('3','john','john@gmail.com','0964543470','102'),
('4','alice','alice@gmail.com','0987123456','103')
Golang - GORM - Mysql
Kết nối
Chúng ta kết nối tới database thông qua connection string với các thông số khi chúng ta khởi tạo database bằng container
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// Định nghĩa struct Student ánh xạ sang bảng student trong database
type Student struct {
Id string `gorm:"primary_key"` // Id là trường mặc định được sử dụng làm khóa chính
FullName string
Email string
Phone string
CardId string
}
// Khai báo các thông số kết nối database
var (
host string = "localhost"
port string = "3306"
username string = "root"
password string = "123"
database string = "db"
)
var DB *gorm.DB
func main() {
// Tạo connection string với các thông số đã được định nghĩa ở trên
connectString := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
username,
password,
host,
port,
database,
)
var err error
// Kết nối tới database dựa vào connection string
DB, err = gorm.Open(mysql.Open(connectString), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), // -> Log các câu lệnh truy vấn database trong terminal
})
// Dừng chương trình nếu quá trình kết nối tới database xảy ra lỗi
if err != nil {
panic("Failed to connect database")
}
}
Table
GORM sẽ tự động chuyển tên struct thành tên bảng theo dạng là snake_case
Ví dụ : Student -> students, User -> users, UserDetail -> user_details
type Student struct {
Id string `gorm:"primary_key"` // Id là trường mặc định được sử dụng làm khóa chính
FullName string
Email string
Phone string
CardId string
}
Chúng ta có thể thay đổi tên bảng default bằng cách implement interface Tabler
type Tabler interface {
TableName() string
}
Vì ở bên trên chúng ta đã tạo bảng student, nên lúc này cần overide tên bảng
func(s *Student) TableName() string{
return "student"
}
Create Object
// Khởi tạo transaction
tx := DB.Begin()
if err := tx.Error; err != nil {
return
}
// Tạo đối tượng dựa trên model
std := model.Student{
Id: NewID(),
FullName: gofakeit.Animal(),
Email: gofakeit.Email(),
Phone: gofakeit.Phone(),
CardId: NewID(),
}
tx.Create(&std)
// Tạo đối tượng với các trường được chỉ định
tx.Select("Id", "FullName", "Email", "CardId").Create(&model.Student{
Id: NewID(),
FullName: gofakeit.Animal(),
Email: gofakeit.Email(),
Phone: gofakeit.Phone(),
CardId: NewID(),
})
// Tạo đối tượng với các thông tin ngoại trừ các thông tin có trong Omit
tx.Omit("Email", "Phone", "CardId").Create(&model.Student{
Id: NewID(),
FullName: gofakeit.Animal(),
Email: gofakeit.Email(),
Phone: gofakeit.Phone(),
CardId: NewID(),
})
// Tạo nhiều đối tượng cùng lúc
var students []model.Student
for i := 0; i < 5; i++ {
newStudent := model.Student{
Id: NewID(),
FullName: gofakeit.Animal(),
Email: gofakeit.Email(),
Phone: gofakeit.Phone(),
CardId: NewID(),
}
students = append(students, newStudent)
}
tx.Create(&students)
// Tạo đối tượng với struct
DB.Model(&model.Student{}).Create(map[string]interface{}{
"Id": NewID(),
"FullName": gofakeit.Animal(),
"Email": gofakeit.Email(),
})
tx.Rollback()
Bên trên là một vài ví dụ về cách chúng ta insert một bản ghi vào database sử dụng GORM. Ở đây chúng ta bắt đầu với việc sử dụng transaction tx := DB.Begin()
và kết thúc bằng tx.Rollback()
để tránh các bản ghi được insert vào trong CSDL.
Trong này mình có bổ sung thêm function NewID để genarate tự động ID dựa trên package gonanoid. và package gofakeit để fake thông tin liên quan đến fullname, email, phone, ...
func NewID() (id string) {
id, _ = gonanoid.New(8) // Random chuỗi ID với độ dài 8 lý tự
return
}
Các bạn có thể cài bằng cách
// Cài đặt gonanoid
go get github.com/matoous/go-nanoid/v2
// Cài đặt gofakeit
go get github.com/brianvoe/gofakeit/v6
Select Object
var std model.Student
// Lấy bản ghi đầu tiên sau khi sắp xếp theo khóa chính
DB.First(&std)
// Lấy bản ghi đầu tiên
DB.Take(&std)
// Lấy bản ghi cuối cùng sau khi sắp xếp theo khóa chính
DB.Last(&std)
// Tìm kiếm theo khóa chính
DB.First(&model.Student{}, "2")
DB.First(&model.Student{}, "id = ?", "2")
// Tìm kiếm theo danh sách khóa chính
DB.Find(&[]model.Student{}, []string{"2", "3", "4"})
// Lấy tất cả bản ghi trong bảng
var students []model.Student
DB.Find(&students)
// Condition
DB.Where("full_name = ?", "bob").First(&model.Student{})
Hook (Object Life Cycle)
Hook là các hàm được gọi trước hoặc sau khi thực hiện các thao tác creation, querying, updating, deletion
Chúng ta có thể chỉ định hook cho một model (struct) cụ thể nào đó, nó sẽ tự động được gọi khi creation, querying, updating, deletion
Nếu khi hook được gọi mà xảy ra lỗi, lúc này GORM sẽ rollback transaction
Hook tạo object
Khi tạo object chúng ta có 4 Hook sau
BeforeSave
BeforeCreate
AfterCreate
AfterSave
Demo Hook BeforeCreate
Trước khi tạo đối tượng, nếu đối tượng chưa có ID thì tự động tạo ID
func (s *Student) BeforeCreate(tx *gorm.DB) (err error) {
if s.Id == "" {
fmt.Println("Bổ sung Id cho student")
s.Id = util.NewID()
}
return
}
Tương tự chúng có các Hook khi thực hiện update, delete, find
Hook Update
BeforeSave
BeforeUpdate
AfterUpdate
AfterSave
Hook Delete
BeforeDelete
AfterDelete
Ví dụ: Nếu student có id = "1" thì không được phép xóa
func (s *Student) BeforeDelete(tx *gorm.DB) (err error) {
if s.Id == "1" {
return errors.New("user not allowed to delete")
}
return
}
Hook Query
AfterFind
Ví dụ: In ra thông tin của student mỗi khi tìm kiếm
func(s *Student) ToString() {
fmt.Println("Id : ", s.Id)
fmt.Println("FullName : ", s.FullName)
fmt.Println("Email : ", s.Email)
fmt.Println("Phone : ", s.Phone)
fmt.Println("CardId : ", s.CardId)
}
func (s *Student) AfterFind(tx *gorm.DB) (err error) {
s.ToString()
return
}
Bình luận