Thao tác CRUD trong Golang

Điều kiện cần

  • Đã biết cài đặt môi trường phát triển và lập trình căn bản với Golang
  • Đã biết cài đặt và sử dụng CSDL MySQL
  • Có hiểu biết về hệ quản trị CSDL Quan hệ và biết cách sử dụng SQL để thao tác với dữ liệu
  • Biết cách thức kết nối Golang với CSDL MySQL

Các thao tác CRUD

Trong lập trình máy tính, CRUD là viết tắt của Create Read Update Delete (Tạo, Đọc/Truy xuất, Cập nhật, Xoá) là bốn chức năng cơ bản của lưu trữ liên tục. Các từ thay thế đôi khi được sử dụng khi xác định bốn chức năng cơ bản của CRUD, chẳng hạn như truy xuất thay vì đọc, sửa đổi thay vì cập nhật hoặc hủy thay vì xóa. CRUD đôi khi cũng được sử dụng để mô tả các quy ước giao diện người dùng tạo điều kiện thuận lợi cho việc xem, tìm kiếm và thay đổi thông tin, thường sử dụng các biểu mẫu và báo cáo dựa trên máy tính.

Thuật ngữ này được phổ biến lần đầu tiên bởi James Martin trong cuốn sách "Managing the Data-base Environment" (Quản lý Môi trường Cơ sở Dữ liệu) năm 1983 của ông.

Từ viết tắt này có thể được mở rộng thành CRUDL để bao gồm danh sách các tập dữ liệu lớn mang lại sự phức tạp hơn như phân trang khi các tập dữ liệu quá lớn để có thể dễ dàng lưu giữ trong bộ nhớ.

Chuẩn bị CSDL trong MySQL

Trước tiên cần cài đặt CSDL MySQL và có tài khoản root và mật khẩu để đăng nhập vào trong MySQL.

Tải tệp OrderDB.sql với liên kết tại đây.

Chạy file OrderDB.sql bằng lệnh sau:

mysql -u root -p < ./OrderDB.sql

Hoặc mở tệp OrderDB.sql bằng MySQLWorkbench với tài khoản root rồi chạy toàn bộ file này.

Tạo cấu trúc dự án

Chúng ta bắt đầu bằng cách đơn giản tạo một thư mục cho dự án của mình và một tệp có tên "main.go" bên trong đó. Sử dụng bằng lệnh sau:

mkdir crud-mysql
cd crud-to-mysql 
touch main.go

Tạo cấu trúc dự án với các bước sau:

  • Tạo module cho dự án:
    go mod init crud​
  • Tạo gói (package) model (chứa các kiểu cấu trúc ánh xạ ORM) cho dự án và file Item.go:
    mkdir model
    cd model 
    touch Item.go
  • Tạo gói (package) dal (Data Access Layer) cho dự án:
    cd ../
    mkdir dal
    cd dal
    
    • Tạo tệp DbUtil.go chứa các hàm tiện ích kết nối với CSDL
      touch DbUtil.go
    • Tạo tệp ItemDal.go chưa các thao tác CRUD cho bảng Items trong CSDL OrderDB
      touch ItemDal.go

Thực hiện mã lệnh cho dự án

Item.go

package model

type Item struct {
	ItemId          int64   `json:"itemId"`
	ItemName        string  `json:"itemName"`
	UnitPrice       float64 `json:"unitPrice"`
	Amount          int32   `json:"amount"`
	ItemStatus      int16   `json:"status"`
	ItemDescription string  `json:"description"`
}

DbUtil.go

package dal

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

//InitializeMySQL to OrderDB
func InitializeMySQL() {
	dBConnection, err := sql.Open("mysql", "sinhnx:sinhnx.dev@tcp(127.0.0.1:3306)/OrderDB")
	if err != nil {
		fmt.Println("Connection Failed!!")
	}
	err = dBConnection.Ping()
	if err != nil {
		fmt.Println("Ping Failed!!")
	}
	db = dBConnection
	dBConnection.SetMaxOpenConns(10)
	dBConnection.SetMaxIdleConns(5)
	dBConnection.SetConnMaxLifetime(time.Second * 10)
}

//GetConnection is get MySQL Connection
func GetConnection() *sql.DB {
	if db == nil {
		InitializeMySQL()
	}
	return db
}

//CloseStmt after run stmt
func CloseStmt(stmt *sql.Stmt) {
	if stmt != nil {
		stmt.Close()
	}
}

//CloseRows when select
func CloseRows(rows *sql.Rows) {
	if rows != nil {
		rows.Close()
	}
}

ItemDal.go

package dal

import (
	"crud/model"
)

//InsertItem to OrderDB
func InsertItem(item model.Item) (int64, int64, error) {
	GetConnection()
	sqlQuery := "INSERT Items SET item_name=?, unit_price=?, amount=?, item_status=?"
	stmt, err := db.Prepare(sqlQuery)
	defer CloseStmt(stmt)
	if err != nil {
		return 0, 0, err
	}
	res, err := stmt.Exec(item.ItemName, item.UnitPrice, item.Amount, item.ItemStatus)
	if err != nil {
		return 0, 0, err
	}
	rowsAffected, err := res.RowsAffected()
	if err != nil {
		return 0, 0, err
	}
	lastInsertId, err := res.LastInsertId()
	return rowsAffected, lastInsertId, err
}

//UpdateItem in OrderDB
func UpdateItem(item model.Item) (int64, error) {
	GetConnection()
	sqlQuery := "UPDATE Items SET item_name=?, unit_price=?, amount=?, item_status=? WHERE item_id = ?"
	stmt, err := db.Prepare(sqlQuery)
	defer CloseStmt(stmt)
	if err != nil {
		return 0, err
	}
	res, err := stmt.Exec(item.ItemName, item.UnitPrice, item.Amount, item.ItemStatus, item.ItemId)
	if err != nil {
		return 0, err
	}
	rowsAffected, err := res.RowsAffected()
	if err != nil {
		return 0, err
	}
	return rowsAffected, err
}

//DeleteItem in OrderDB
func DeleteItem(itemId int64) (int64, error) {
	GetConnection()
	sqlQuery := "DELETE FROM Items WHERE item_id = ?"
	stmt, err := db.Prepare(sqlQuery)
	defer CloseStmt(stmt)
	if err != nil {
		return 0, err
	}
	res, err := stmt.Exec(itemId)
	if err != nil {
		return 0, err
	}
	rowsAffected, err := res.RowsAffected()
	if err != nil {
		return 0, err
	}
	return rowsAffected, err
}

//GetItem from itemId
func GetItem(itemId int64) (model.Item, error) {
	sqlQuery := "SELECT item_id, item_name, unit_price, amount, item_status, item_description FROM Items WHERE item_id = ?"
	stmt, err := db.Prepare(sqlQuery)
	defer CloseStmt(stmt)
	var item model.Item
	if err != nil {
		return item, err
	}
	res, err := stmt.Query(itemId)
	defer CloseRows(res)
	if err != nil {
		return item, err
	}
	if res.Next() {
		res.Scan(&item.ItemId, &item.ItemName, &item.UnitPrice, &item.Amount, &item.ItemStatus, &item.ItemDescription)
	}
	return item, err
}

main.go

package main

import (
	"crud/dal"
	"crud/model"
	"fmt"
)

func main() {
	item := model.Item{0, "Item 100", 15.5, 12, 1, ""}
	rowsAffected, lastInsertedId, err := dal.InsertItem(item)

	if err != nil {
		fmt.Println("Insert error.")
	} else {
		if rowsAffected > 0 {
			fmt.Println("Insert completed.")
			item.ItemId = lastInsertedId
			fmt.Printf("new item id is %d\n", item.ItemId)
		}
	}

	//update item
	item.ItemName = "Item 10001"
	rowsUpdatedAffected, err := dal.UpdateItem(item)
	if err != nil {
		fmt.Println("Update error.")
	} else {
		if rowsUpdatedAffected > 0 {
			fmt.Println("update completed.")
		}
	}

	//get item
	lastInsertedItem, err := dal.GetItem(lastInsertedId)
	if err != nil {
		fmt.Println("get error.")
	} else {
		fmt.Printf("%d - %s - %f - %d\n", lastInsertedItem.ItemId, lastInsertedItem.ItemName, lastInsertedItem.UnitPrice, item.Amount)
	}

	//delete item
	rowsDeletedAffected, err := dal.DeleteItem(lastInsertedId)
	if err != nil {
		fmt.Println("delete error.")
	} else {
		if rowsDeletedAffected > 0 {
			fmt.Println("delete completed.")
		}
	}
}

Biên dịch và chạy dự án

Gõ lệnh sau để biên dịch và chạy dự án

go build
./crud

Kết quả hiển thị:

Insert completed.
new item id is 20
update completed.
20 - Item 10001 - 15.500000 - 12
delete completed.