☰ Mục lục series
Tài liệu Go tiếng Việt
  1. Bắt đầu (Getting started)
    1. Tổng quan
    2. Tải về và cài đặt
    3. Bắt đầu với Go
    4. Quản lý các bản cài đặt Go
    5. Plugin trình soạn thảo và IDE
  2. Dạo quanh Go (A Tour of Go)
    1. Chào mừng
    2. Package, biến và hàm
    3. Điều khiển luồng
    4. Struct, slice và map
    5. Method và interface
    6. Generics
    7. Xử lý đồng thời
  3. Hướng dẫn (Tutorials)
    1. Danh sách hướng dẫn
    2. Tạo một Go module
    3. Gọi mã của bạn từ một module khác
    4. Trả về và xử lý lỗi
    5. Trả về lời chào ngẫu nhiên
    6. Trả về lời chào cho nhiều người
    7. Thêm một test
    8. Biên dịch và cài đặt ứng dụng
    9. Kết luận
    10. Bắt đầu với workspace nhiều module
    11. Phát triển RESTful API với Go và Gin
    12. Truy cập cơ sở dữ liệu quan hệ
    13. Bắt đầu với generics
    14. Bắt đầu với fuzzing
    15. Làm việc với JSON
    16. Tìm và sửa các dependency có lỗ hổng với govulncheck
    17. Tìm và khắc phục phụ thuộc có lỗ hổng với VS Code Go
    18. Viết ứng dụng web
  4. Viết code Go
    1. Cách viết mã Go
    2. Effective Go
    3. Doc Comment trong Go
  5. Module
    1. Phát triển và publish module
    2. Quản lý phụ thuộc
    3. Tổ chức một module Go
    4. Quản lý mã nguồn của module
    5. Cách đánh số phiên bản module
    6. Quy trình phát hành và đánh phiên bản module
    7. Publish một module
    8. Phát triển một bản cập nhật major version
    9. Tham chiếu tệp go.mod
    10. Ngừng hỗ trợ 'go get' cho việc cài đặt tệp thực thi
  6. Cơ sở dữ liệu (Database)
    1. Truy cập cơ sở dữ liệu quan hệ
    2. Mở một handle cơ sở dữ liệu
    3. Thực thi câu lệnh SQL không trả về dữ liệu
    4. Truy vấn dữ liệu
    5. Dùng prepared statement
    6. Thực thi transaction
    7. Huỷ các thao tác đang chạy
    8. Quản lý kết nối
    9. Tránh rủi ro SQL injection
  7. Chẩn đoán và hiệu năng
    1. Chẩn đoán
    2. Hướng dẫn về Go Garbage Collector
    3. Tối ưu hoá dựa trên profile
    4. Data Race Detector
    5. Gỡ lỗi mã Go bằng GDB
    6. Hỗ trợ coverage profiling cho integration test
  8. Bảo mật (Security)
    1. Tổng quan bảo mật
    2. Thực hành tốt về bảo mật cho lập trình viên Go
    3. Quản lý lỗ hổng trong Go
    4. Cơ sở dữ liệu lỗ hổng của Go
    5. Quét lỗ hổng trong IDE
    6. Chính sách CNA của Go
    7. Go Fuzzing
    8. Chi tiết kỹ thuật của Go Fuzzing
    9. Chính sách bảo mật của Go
    10. Threat Model của Go
    11. Các quyết định về bảo mật của Go
    12. Tuân thủ FIPS 140-3
  9. Công cụ và toolchain
    1. Tài liệu về các lệnh
    2. Về lệnh go
    3. Go Toolchains
    4. Telemetry của Go
    5. Cài đặt Go từ mã nguồn
    6. Cài đặt và sử dụng gccgo
  10. Tham khảo
    1. Câu hỏi thường gặp (FAQ)
    2. Go 1 và tương lai của các chương trình Go
    3. Hướng dẫn chuyển đổi sang encoding/json/v2
    4. Hướng dẫn đóng góp
    5. Đóng góp cho frontend gccgo
  11. Phụ lục
    1. Bảng thuật ngữ
Hướng dẫn (Tutorials)

Hướng dẫn: Truy cập cơ sở dữ liệu quan hệ

Bản dịch từ go.dev/doc/tutorial/database-access

Hướng dẫn này giới thiệu những kiến thức cơ bản về việc truy cập cơ sở dữ liệu quan hệ bằng Go và package database/sql trong thư viện chuẩn.

Bạn sẽ thu được nhiều nhất từ hướng dẫn này nếu đã quen cơ bản với Go và bộ công cụ của nó. Nếu đây là lần đầu bạn tiếp xúc với Go, hãy xem Hướng dẫn: Bắt đầu với Go để có phần giới thiệu nhanh.

Package database/sql mà bạn sẽ dùng bao gồm các kiểu dữ liệu và hàm để kết nối tới cơ sở dữ liệu, thực thi transaction (giao dịch), huỷ một thao tác đang chạy, và nhiều thứ khác. Để biết thêm chi tiết về cách dùng package này, xem Truy cập cơ sở dữ liệu.

Trong hướng dẫn này, bạn sẽ tạo một cơ sở dữ liệu, rồi viết mã nguồn để truy cập cơ sở dữ liệu đó. Dự án ví dụ của bạn sẽ là một kho dữ liệu về các đĩa nhạc jazz cổ.

Trong hướng dẫn này, bạn sẽ lần lượt đi qua các phần sau:

  1. Tạo một thư mục cho mã nguồn của bạn.
  2. Thiết lập cơ sở dữ liệu.
  3. Import database driver.
  4. Lấy database handle và kết nối.
  5. Truy vấn nhiều row (dòng dữ liệu).
  6. Truy vấn một row duy nhất.
  7. Thêm dữ liệu.

Ghi chú: Để xem các hướng dẫn khác, xem Hướng dẫn.

Điều kiện cần

  • Đã cài đặt hệ quản trị cơ sở dữ liệu quan hệ (DBMS) MySQL.
  • Đã cài đặt Go. Để biết hướng dẫn cài đặt, xem Cài đặt Go.
  • Một công cụ để soạn mã nguồn. Bất kỳ trình soạn thảo văn bản nào bạn có đều dùng tốt.
  • Một cửa sổ dòng lệnh. Go hoạt động tốt với mọi terminal trên Linux và Mac, và với PowerShell hoặc cmd trên Windows.

Tạo một thư mục cho mã nguồn

Để bắt đầu, hãy tạo một thư mục chứa mã nguồn mà bạn sẽ viết.

  1. Mở dòng lệnh và chuyển về thư mục home của bạn.

    Trên Linux hoặc Mac:

    $ cd

    Trên Windows:

    C:\> cd %HOMEPATH%

    Trong phần còn lại của hướng dẫn, chúng tôi sẽ hiển thị dấu $ làm dấu nhắc lệnh. Các câu lệnh chúng tôi dùng cũng sẽ chạy được trên Windows.

  2. Từ dòng lệnh, tạo một thư mục cho mã nguồn của bạn tên là data-access.

    $ mkdir data-access
    $ cd data-access
  3. Tạo một module để bạn có thể quản lý các phụ thuộc mà bạn sẽ thêm vào trong hướng dẫn này.

    Chạy câu lệnh go mod init, truyền cho nó đường dẫn module của mã nguồn mới.

    $ go mod init example/data-access
    go: creating new go.mod: module example/data-access

    Câu lệnh này tạo ra tệp go.mod, nơi các phụ thuộc bạn thêm vào sẽ được liệt kê để theo dõi. Để biết thêm, hãy xem Quản lý phụ thuộc.

    Ghi chú: Trong phát triển thực tế, bạn sẽ chỉ định một đường dẫn module phù hợp hơn với nhu cầu riêng của mình. Để biết thêm, xem Quản lý phụ thuộc.

Tiếp theo, bạn sẽ tạo một cơ sở dữ liệu.

Thiết lập cơ sở dữ liệu

Ở bước này, bạn sẽ tạo cơ sở dữ liệu mà bạn sẽ làm việc cùng. Bạn sẽ dùng chính CLI của DBMS để tạo cơ sở dữ liệu và bảng, cũng như để thêm dữ liệu.

Bạn sẽ tạo một cơ sở dữ liệu chứa dữ liệu về các bản thu jazz cổ trên đĩa than.

Mã nguồn ở đây dùng MySQL CLI, nhưng hầu hết các DBMS đều có CLI riêng với tính năng tương tự.

  1. Mở một cửa sổ dòng lệnh mới.

  2. Tại dòng lệnh, đăng nhập vào DBMS của bạn, như ví dụ sau đây cho MySQL.

    $ mysql -u root -p
    Enter password:
    
    mysql>
  3. Tại dấu nhắc lệnh mysql, tạo một cơ sở dữ liệu.

    mysql> create database recordings;
  4. Chuyển sang cơ sở dữ liệu bạn vừa tạo để có thể thêm bảng.

    mysql> use recordings;
    Database changed
  5. Trong trình soạn thảo văn bản, ở thư mục data-access, tạo một tệp tên là create-tables.sql để chứa script SQL dùng để thêm bảng.

  6. Dán đoạn mã SQL sau vào tệp, rồi lưu tệp lại.

    DROP TABLE IF EXISTS album;
    CREATE TABLE album (
      id         INT AUTO_INCREMENT NOT NULL,
      title      VARCHAR(128) NOT NULL,
      artist     VARCHAR(255) NOT NULL,
      price      DECIMAL(5,2) NOT NULL,
      PRIMARY KEY (`id`)
    );
    
    INSERT INTO album
      (title, artist, price)
    VALUES
      ('Blue Train', 'John Coltrane', 56.99),
      ('Giant Steps', 'John Coltrane', 63.99),
      ('Jeru', 'Gerry Mulligan', 17.99),
      ('Sarah Vaughan', 'Sarah Vaughan', 34.98);

    Trong đoạn mã SQL này, bạn:

    • Xoá (drop) bảng tên là album. Việc thực thi câu lệnh này trước giúp bạn dễ dàng chạy lại script sau này nếu muốn làm lại bảng từ đầu.

    • Tạo bảng album với bốn cột: title, artist, và price. Giá trị id của mỗi row được DBMS tạo tự động.

    • Thêm bốn row với các giá trị.

  7. Từ dấu nhắc lệnh mysql, chạy script bạn vừa tạo.

    Bạn sẽ dùng câu lệnh source theo dạng sau:

    mysql> source /path/to/create-tables.sql
  8. Tại dấu nhắc lệnh của DBMS, dùng câu lệnh SELECT để kiểm chứng rằng bạn đã tạo thành công bảng cùng dữ liệu.

    mysql> select * from album;
    +----+---------------+----------------+-------+
    | id | title         | artist         | price |
    +----+---------------+----------------+-------+
    |  1 | Blue Train    | John Coltrane  | 56.99 |
    |  2 | Giant Steps   | John Coltrane  | 63.99 |
    |  3 | Jeru          | Gerry Mulligan | 17.99 |
    |  4 | Sarah Vaughan | Sarah Vaughan  | 34.98 |
    +----+---------------+----------------+-------+
    4 rows in set (0.00 sec)

Tiếp theo, bạn sẽ viết một ít mã Go để kết nối, từ đó có thể truy vấn dữ liệu.

Tìm và import database driver

Giờ bạn đã có một cơ sở dữ liệu với ít dữ liệu, hãy bắt đầu với mã Go.

Hãy tìm và import một database driver — thành phần sẽ chuyển các yêu cầu bạn tạo ra thông qua các hàm trong package database/sql thành các yêu cầu mà cơ sở dữ liệu hiểu được.

  1. Trong trình duyệt, truy cập trang wiki SQLDrivers để xác định driver bạn có thể dùng.

    Dùng danh sách trên trang đó để chọn driver bạn sẽ dùng. Để truy cập MySQL trong hướng dẫn này, bạn sẽ dùng Go-MySQL-Driver.

  2. Ghi nhớ tên package của driver – ở đây là github.com/go-sql-driver/mysql.

  3. Dùng trình soạn thảo văn bản, tạo một tệp để viết mã Go và lưu tệp đó với tên main.go trong thư mục data-access mà bạn đã tạo trước đó.

  4. Dán đoạn mã sau vào main.go để import package driver.

    package main
    
    import "github.com/go-sql-driver/mysql"

    Trong đoạn mã này, bạn:

    • Đặt mã nguồn của bạn vào package main để có thể thực thi độc lập.

    • Import MySQL driver github.com/go-sql-driver/mysql.

Sau khi đã import driver, bạn sẽ bắt đầu viết mã để truy cập cơ sở dữ liệu.

Lấy database handle và kết nối

Giờ hãy viết một ít mã Go để có quyền truy cập cơ sở dữ liệu thông qua một database handle (đối tượng đại diện cho quyền truy cập cơ sở dữ liệu).

Bạn sẽ dùng một pointer tới struct sql.DB, đại diện cho quyền truy cập tới một cơ sở dữ liệu cụ thể.

Viết mã

  1. Trong main.go, bên dưới đoạn mã import bạn vừa thêm, dán đoạn mã Go sau để tạo một database handle.

    var db *sql.DB
    
    func main() {
    	// Capture connection properties.
    	cfg := mysql.NewConfig()
    	cfg.User = os.Getenv("DBUSER")
    	cfg.Passwd = os.Getenv("DBPASS")
    	cfg.Net = "tcp"
    	cfg.Addr = "127.0.0.1:3306"
    	cfg.DBName = "recordings"
    
    	// Get a database handle.
    	var err error
    	db, err = sql.Open("mysql", cfg.FormatDSN())
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	pingErr := db.Ping()
    	if pingErr != nil {
    		log.Fatal(pingErr)
    	}
    	fmt.Println("Connected!")
    }

    Trong đoạn mã này, bạn:

    • Khai báo biến db kiểu *sql.DB. Đây chính là database handle của bạn.

      Việc đặt db làm biến toàn cục giúp ví dụ này đơn giản hơn. Trong môi trường production, bạn nên tránh dùng biến toàn cục, chẳng hạn bằng cách truyền biến đó vào các hàm cần dùng, hoặc bọc nó trong một struct.

    • Dùng Config của MySQL driver – cùng với phương thức FormatDSN của kiểu đó -– để thu thập các thuộc tính kết nối và định dạng chúng thành một DSN (chuỗi mô tả nguồn dữ liệu) cho chuỗi kết nối.

      Struct Config cho ra đoạn mã dễ đọc hơn so với việc viết thẳng một chuỗi kết nối.

    • Gọi sql.Open để khởi tạo biến db, truyền vào giá trị trả về của FormatDSN.

    • Kiểm tra lỗi từ sql.Open. Nó có thể thất bại nếu, ví dụ, các thông số kết nối cơ sở dữ liệu của bạn không đúng định dạng.

      Để đơn giản hoá mã nguồn, bạn gọi log.Fatal để kết thúc chương trình và in lỗi ra console. Trong mã nguồn production, bạn sẽ muốn xử lý lỗi một cách mềm mại hơn.

    • Gọi DB.Ping để xác nhận rằng việc kết nối tới cơ sở dữ liệu hoạt động. Khi chạy, sql.Open có thể không kết nối ngay lập tức, tuỳ theo driver. Ở đây bạn dùng Ping để xác nhận rằng package database/sql có thể kết nối khi cần.

    • Kiểm tra lỗi từ Ping, phòng trường hợp kết nối thất bại.

    • In ra một thông báo nếu Ping kết nối thành công.

  2. Gần đầu tệp main.go, ngay bên dưới khai báo package, hãy import các package cần thiết để hỗ trợ đoạn mã bạn vừa viết.

    Phần đầu tệp giờ sẽ trông như sau:

    package main
    
    import (
    	"database/sql"
    	"fmt"
    	"log"
    	"os"
    
    	"github.com/go-sql-driver/mysql"
    )
  3. Lưu main.go.

Chạy mã

  1. Bắt đầu theo dõi module MySQL driver như một phụ thuộc.

    Dùng go get để thêm module github.com/go-sql-driver/mysql làm phụ thuộc cho module của bạn. Dùng đối số dấu chấm với ý nghĩa “lấy các phụ thuộc cho mã nguồn trong thư mục hiện tại.”

    $ go get .
    go: added filippo.io/edwards25519 v1.1.0
    go: added github.com/go-sql-driver/mysql v1.8.1

    Go đã tải phụ thuộc này về vì bạn đã thêm nó vào khai báo import ở bước trước. Để biết thêm về việc theo dõi phụ thuộc, xem Thêm một phụ thuộc.

  2. Từ dòng lệnh, đặt các biến môi trường DBUSER và DBPASS để chương trình Go sử dụng.

    Trên Linux hoặc Mac:

    $ export DBUSER=username
    $ export DBPASS=password

    Trên Windows:

    C:\Users\you\data-access> set DBUSER=username
    C:\Users\you\data-access> set DBPASS=password
  3. Từ dòng lệnh trong thư mục chứa main.go, chạy mã nguồn bằng cách gõ go run với đối số dấu chấm, mang ý nghĩa “chạy package trong thư mục hiện tại.”

    $ go run .
    Connected!

Bạn đã kết nối được! Tiếp theo, bạn sẽ truy vấn một ít dữ liệu.

Truy vấn nhiều row

Ở phần này, bạn sẽ dùng Go để thực thi một truy vấn SQL được thiết kế để trả về nhiều row.

Với các câu lệnh SQL có thể trả về nhiều row, bạn dùng phương thức Query của package database/sql, rồi lặp qua các row mà nó trả về. (Bạn sẽ học cách truy vấn một row duy nhất ở phần sau, trong mục Truy vấn một row duy nhất.)

Viết mã

  1. Trong main.go, ngay phía trên func main, dán định nghĩa sau của struct Album. Bạn sẽ dùng nó để chứa dữ liệu của row trả về từ truy vấn.

    type Album struct {
    	ID     int64
    	Title  string
    	Artist string
    	Price  float32
    }
  2. Bên dưới func main, dán hàm albumsByArtist sau đây để truy vấn cơ sở dữ liệu.

    // albumsByArtist queries for albums that have the specified artist name.
    func albumsByArtist(name string) ([]Album, error) {
    	// An albums slice to hold data from returned rows.
    	var albums []Album
    
    	rows, err := db.Query("SELECT * FROM album WHERE artist = ?", name)
    	if err != nil {
    		return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
    	}
    	defer rows.Close()
    	// Loop through rows, using Scan to assign column data to struct fields.
    	for rows.Next() {
    		var alb Album
    		if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
    			return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
    		}
    		albums = append(albums, alb)
    	}
    	if err := rows.Err(); err != nil {
    		return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
    	}
    	return albums, nil
    }

    Trong đoạn mã này, bạn:

    • Khai báo một slice albums kiểu Album mà bạn đã định nghĩa. Nó sẽ chứa dữ liệu từ các row trả về. Tên và kiểu của các trường trong struct tương ứng với tên và kiểu của các cột trong cơ sở dữ liệu.

    • Dùng DB.Query để thực thi câu lệnh SELECT nhằm truy vấn các album có tên nghệ sĩ được chỉ định.

      Tham số đầu tiên của Query là câu lệnh SQL. Sau tham số đó, bạn có thể truyền không hoặc nhiều tham số thuộc kiểu bất kỳ. Chúng cho bạn chỗ để chỉ định giá trị cho các tham số trong câu lệnh SQL. Bằng cách tách câu lệnh SQL khỏi giá trị của tham số (thay vì nối chuỗi chúng lại, chẳng hạn bằng fmt.Sprintf), bạn cho phép package database/sql gửi các giá trị tách rời khỏi phần văn bản SQL, loại bỏ mọi rủi ro SQL injection.

    • Defer việc đóng rows để mọi tài nguyên mà nó giữ sẽ được giải phóng khi hàm kết thúc.

    • Lặp qua các row trả về, dùng Rows.Scan để gán giá trị các cột của mỗi row vào các trường của struct Album.

      Scan nhận một danh sách pointer tới các giá trị Go, nơi giá trị của các cột sẽ được ghi vào. Ở đây, bạn truyền các pointer tới những trường trong biến alb, được tạo bằng toán tử &. Scan ghi thông qua các pointer đó để cập nhật các trường của struct.

    • Bên trong vòng lặp, kiểm tra lỗi khi scan giá trị các cột vào các trường của struct.

    • Bên trong vòng lặp, thêm alb mới vào slice albums.

    • Sau vòng lặp, kiểm tra lỗi của toàn bộ truy vấn bằng rows.Err. Lưu ý rằng nếu bản thân truy vấn thất bại, kiểm tra lỗi ở đây là cách duy nhất để biết rằng kết quả chưa đầy đủ.

  3. Cập nhật hàm main để gọi albumsByArtist.

    Ở cuối func main, thêm đoạn mã sau.

    albums, err := albumsByArtist("John Coltrane")
    if err != nil {
    	log.Fatal(err)
    }
    fmt.Printf("Albums found: %v\n", albums)

    Trong đoạn mã mới, giờ bạn:

    • Gọi hàm albumsByArtist mà bạn đã thêm, gán giá trị trả về của nó cho biến mới albums.

    • In kết quả ra.

Chạy mã

Từ dòng lệnh trong thư mục chứa main.go, chạy mã nguồn.

$ go run .
Connected!
Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]

Tiếp theo, bạn sẽ truy vấn một row duy nhất.

Truy vấn một row duy nhất

Ở phần này, bạn sẽ dùng Go để truy vấn một row duy nhất trong cơ sở dữ liệu.

Với các câu lệnh SQL mà bạn biết chắc sẽ trả về nhiều nhất một row, bạn có thể dùng QueryRow, đơn giản hơn so với việc dùng vòng lặp với Query.

Viết mã

  1. Bên dưới albumsByArtist, dán hàm albumByID sau đây.

    // albumByID queries for the album with the specified ID.
    func albumByID(id int64) (Album, error) {
    	// An album to hold data from the returned row.
    	var alb Album
    
    	row := db.QueryRow("SELECT * FROM album WHERE id = ?", id)
    	if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
    		if err == sql.ErrNoRows {
    			return alb, fmt.Errorf("albumsById %d: no such album", id)
    		}
    		return alb, fmt.Errorf("albumsById %d: %v", id, err)
    	}
    	return alb, nil
    }

    Trong đoạn mã này, bạn:

    • Dùng DB.QueryRow để thực thi câu lệnh SELECT nhằm truy vấn album có ID được chỉ định.

      Nó trả về một sql.Row. Để đơn giản hoá mã nguồn gọi nó (chính là mã của bạn!), QueryRow không trả về lỗi. Thay vào đó, nó thu xếp để trả về mọi lỗi truy vấn (chẳng hạn sql.ErrNoRows) từ Rows.Scan sau đó.

    • Dùng Row.Scan để sao chép giá trị các cột vào các trường của struct.

    • Kiểm tra lỗi từ Scan.

      Lỗi đặc biệt sql.ErrNoRows cho biết truy vấn không trả về row nào. Thông thường, lỗi đó đáng được thay bằng một thông báo cụ thể hơn, chẳng hạn “no such album” như ở đây.

  2. Cập nhật main để gọi albumByID.

    Ở cuối func main, thêm đoạn mã sau.

    // Hard-code ID 2 here to test the query.
    alb, err := albumByID(2)
    if err != nil {
    	log.Fatal(err)
    }
    fmt.Printf("Album found: %v\n", alb)

    Trong đoạn mã mới, giờ bạn:

    • Gọi hàm albumByID mà bạn đã thêm.

    • In ra ID của album trả về.

Chạy mã

Từ dòng lệnh trong thư mục chứa main.go, chạy mã nguồn.

$ go run .
Connected!
Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
Album found: {2 Giant Steps John Coltrane 63.99}

Tiếp theo, bạn sẽ thêm một album vào cơ sở dữ liệu.

Thêm dữ liệu

Ở phần này, bạn sẽ dùng Go để thực thi câu lệnh SQL INSERT nhằm thêm một row mới vào cơ sở dữ liệu.

Bạn đã thấy cách dùng Query và QueryRow với các câu lệnh SQL có trả về dữ liệu. Để thực thi các câu lệnh SQL không trả về dữ liệu, bạn dùng Exec.

Viết mã

  1. Bên dưới albumByID, dán hàm addAlbum sau đây để chèn một album mới vào cơ sở dữ liệu, rồi lưu tệp main.go.

    // addAlbum adds the specified album to the database,
    // returning the album ID of the new entry
    func addAlbum(alb Album) (int64, error) {
    	result, err := db.Exec("INSERT INTO album (title, artist, price) VALUES (?, ?, ?)", alb.Title, alb.Artist, alb.Price)
    	if err != nil {
    		return 0, fmt.Errorf("addAlbum: %v", err)
    	}
    	id, err := result.LastInsertId()
    	if err != nil {
    		return 0, fmt.Errorf("addAlbum: %v", err)
    	}
    	return id, nil
    }

    Trong đoạn mã này, bạn:

    • Dùng DB.Exec để thực thi câu lệnh INSERT.

      Giống như Query, Exec nhận một câu lệnh SQL, theo sau là các giá trị tham số cho câu lệnh SQL đó.

    • Kiểm tra lỗi từ thao tác INSERT.

    • Lấy ID của row vừa được chèn vào cơ sở dữ liệu bằng Result.LastInsertId.

    • Kiểm tra lỗi từ thao tác lấy ID.

  2. Cập nhật main để gọi hàm addAlbum mới.

    Ở cuối func main, thêm đoạn mã sau.

    albID, err := addAlbum(Album{
    	Title:  "The Modern Sound of Betty Carter",
    	Artist: "Betty Carter",
    	Price:  49.99,
    })
    if err != nil {
    	log.Fatal(err)
    }
    fmt.Printf("ID of added album: %v\n", albID)

    Trong đoạn mã mới, giờ bạn:

    • Gọi addAlbum với một album mới, gán ID của album bạn đang thêm vào biến albID.

Chạy mã

Từ dòng lệnh trong thư mục chứa main.go, chạy mã nguồn.

$ go run .
Connected!
Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
Album found: {2 Giant Steps John Coltrane 63.99}
ID of added album: 5

Kết luận

Chúc mừng! Bạn vừa dùng Go để thực hiện các thao tác đơn giản với một cơ sở dữ liệu quan hệ.

Các chủ đề tiếp theo được gợi ý:

  • Hãy xem qua hướng dẫn truy cập dữ liệu, trong đó có thêm thông tin về những chủ đề chỉ được đề cập sơ lược ở đây.

  • Nếu bạn mới làm quen với Go, bạn sẽ tìm thấy những thực hành tốt hữu ích được mô tả trong Effective Go và Cách viết mã Go.

  • Go Tour là phần giới thiệu từng bước rất hay về các kiến thức nền tảng của Go.

Mã nguồn hoàn chỉnh

Phần này chứa mã nguồn của ứng dụng mà bạn xây dựng trong hướng dẫn này.

package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"

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

var db *sql.DB

type Album struct {
	ID     int64
	Title  string
	Artist string
	Price  float32
}

func main() {
	// Capture connection properties.
	cfg := mysql.NewConfig()
	cfg.User = os.Getenv("DBUSER")
	cfg.Passwd = os.Getenv("DBPASS")
	cfg.Net = "tcp"
	cfg.Addr = "127.0.0.1:3306"
	cfg.DBName = "recordings"

	// Get a database handle.
	var err error
	db, err = sql.Open("mysql", cfg.FormatDSN())
	if err != nil {
		log.Fatal(err)
	}

	pingErr := db.Ping()
	if pingErr != nil {
		log.Fatal(pingErr)
	}
	fmt.Println("Connected!")

	albums, err := albumsByArtist("John Coltrane")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Albums found: %v\n", albums)

	// Hard-code ID 2 here to test the query.
	alb, err := albumByID(2)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Album found: %v\n", alb)

	albID, err := addAlbum(Album{
		Title:  "The Modern Sound of Betty Carter",
		Artist: "Betty Carter",
		Price:  49.99,
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("ID of added album: %v\n", albID)
}

// albumsByArtist queries for albums that have the specified artist name.
func albumsByArtist(name string) ([]Album, error) {
	// An albums slice to hold data from returned rows.
	var albums []Album

	rows, err := db.Query("SELECT * FROM album WHERE artist = ?", name)
	if err != nil {
		return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
	}
	defer rows.Close()
	// Loop through rows, using Scan to assign column data to struct fields.
	for rows.Next() {
		var alb Album
		if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
			return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
		}
		albums = append(albums, alb)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
	}
	return albums, nil
}

// albumByID queries for the album with the specified ID.
func albumByID(id int64) (Album, error) {
	// An album to hold data from the returned row.
	var alb Album

	row := db.QueryRow("SELECT * FROM album WHERE id = ?", id)
	if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
		if err == sql.ErrNoRows {
			return alb, fmt.Errorf("albumsById %d: no such album", id)
		}
		return alb, fmt.Errorf("albumsById %d: %v", id, err)
	}
	return alb, nil
}

// addAlbum adds the specified album to the database,
// returning the album ID of the new entry
func addAlbum(alb Album) (int64, error) {
	result, err := db.Exec("INSERT INTO album (title, artist, price) VALUES (?, ?, ?)", alb.Title, alb.Artist, alb.Price)
	if err != nil {
		return 0, fmt.Errorf("addAlbum: %v", err)
	}
	id, err := result.LastInsertId()
	if err != nil {
		return 0, fmt.Errorf("addAlbum: %v", err)
	}
	return id, nil
}

Nội dung gốc: Go Documentation © The Go Authors, giấy phép CC BY 3.0. Bản dịch và ghi chú: Lê Văn Dũng.