☰ 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ữ
Cơ sở dữ liệu (Database)

Thực thi transaction

Bản dịch từ go.dev/doc/database/execute-transactions

Bạn có thể thực thi các transaction cơ sở dữ liệu bằng sql.Tx,, kiểu đại diện cho một transaction. Ngoài các method Commit và Rollback thể hiện ngữ nghĩa riêng của transaction, sql.Tx còn có đầy đủ các method mà bạn dùng để thực hiện những thao tác cơ sở dữ liệu thông thường. Để lấy sql.Tx, bạn gọi DB.Begin hoặc DB.BeginTx.

Một transaction cơ sở dữ liệu gom nhiều thao tác lại thành một phần của một mục tiêu lớn hơn. Tất cả các thao tác đều phải thành công, hoặc không thao tác nào được thực hiện, và trong cả hai trường hợp tính toàn vẹn của dữ liệu đều được giữ nguyên. Thông thường, quy trình của một transaction gồm:

  1. Bắt đầu transaction.
  2. Thực hiện một loạt thao tác cơ sở dữ liệu.
  3. Nếu không có lỗi xảy ra, commit transaction để áp dụng các thay đổi lên cơ sở dữ liệu.
  4. Nếu có lỗi xảy ra, rollback transaction để giữ nguyên cơ sở dữ liệu không thay đổi.

Package sql cung cấp các method để bắt đầu và kết thúc một transaction, cũng như các method để thực hiện những thao tác cơ sở dữ liệu ở giữa. Các method này tương ứng với bốn bước trong quy trình nêu trên.

  • Bắt đầu một transaction.

    DB.Begin hoặc DB.BeginTx bắt đầu một transaction cơ sở dữ liệu mới, trả về một sql.Tx đại diện cho nó.

  • Thực hiện các thao tác cơ sở dữ liệu.

    Với một sql.Tx, bạn có thể truy vấn hoặc cập nhật cơ sở dữ liệu qua một loạt thao tác dùng chung một kết nối duy nhất. Để hỗ trợ điều này, Tx xuất ra những method sau:

  • Kết thúc transaction bằng một trong hai cách sau:

    • Commit transaction bằng Tx.Commit.

      Nếu Commit thành công (trả về lỗi nil), thì toàn bộ kết quả truy vấn được xác nhận là hợp lệ và mọi cập nhật đã thực thi đều được áp dụng lên cơ sở dữ liệu như một thay đổi nguyên tử duy nhất. Nếu Commit thất bại, thì mọi kết quả từ Query và Exec trên Tx đó nên bị xem là không hợp lệ và loại bỏ.

    • Rollback transaction bằng Tx.Rollback.

      Ngay cả khi Tx.Rollback thất bại, transaction cũng sẽ không còn hợp lệ nữa, và nó cũng chưa từng được commit vào cơ sở dữ liệu.

Thực hành tốt

Hãy làm theo những thực hành tốt dưới đây để xoay xở tốt hơn với phần ngữ nghĩa phức tạp và việc quản lý kết nối mà transaction đôi khi đòi hỏi.

  • Dùng các API được mô tả trong phần này để quản lý transaction. Không dùng trực tiếp các câu lệnh SQL liên quan đến transaction như BEGIN và COMMIT — làm vậy có thể khiến cơ sở dữ liệu của bạn rơi vào trạng thái không lường trước được, nhất là trong các chương trình chạy đồng thời.
  • Khi đang dùng một transaction, hãy cẩn thận đừng gọi trực tiếp thêm các method sql.DB ngoài transaction, vì chúng sẽ được thực thi bên ngoài transaction, khiến mã của bạn thấy một trạng thái cơ sở dữ liệu không nhất quán, hoặc thậm chí gây ra deadlock.

Ví dụ

Mã trong ví dụ dưới đây dùng một transaction để tạo một đơn hàng mới của khách cho một album. Trong quá trình đó, mã sẽ:

  1. Bắt đầu một transaction.
  2. Defer việc rollback transaction. Nếu transaction thành công, nó sẽ được commit trước khi hàm kết thúc, khiến lệnh gọi rollback đã defer trở thành vô hiệu. Nếu transaction thất bại thì nó sẽ không được commit, nghĩa là rollback sẽ được gọi khi hàm kết thúc.
  3. Xác nhận rằng tồn kho đủ cho album mà khách đang đặt.
  4. Nếu đủ, cập nhật số lượng tồn kho, giảm đi theo số album đã đặt.
  5. Tạo một đơn hàng mới và lấy ID sinh ra của đơn hàng mới cho client.
  6. Commit transaction và trả về ID.

Ví dụ này dùng các method của Tx nhận đối số context.Context. Nhờ đó, việc thực thi của hàm – bao gồm cả các thao tác cơ sở dữ liệu – có thể bị huỷ nếu nó chạy quá lâu hoặc kết nối của client bị đóng. Tìm hiểu thêm tại Huỷ các thao tác đang chạy.

// CreateOrder creates an order for an album and returns the new order ID.
func CreateOrder(ctx context.Context, albumID, quantity, custID int) (orderID int64, err error) {

	// Create a helper function for preparing failure results.
	fail := func(err error) (int64, error) {
		return 0, fmt.Errorf("CreateOrder: %v", err)
	}

	// Get a Tx for making transaction requests.
	tx, err := db.BeginTx(ctx, nil)
	if err != nil {
		return fail(err)
	}
	// Defer a rollback in case anything fails.
	defer tx.Rollback()

	// Confirm that album inventory is enough for the order.
	var enough bool
	if err = tx.QueryRowContext(ctx, "SELECT (quantity >= ?) from album where id = ?",
		quantity, albumID).Scan(&enough); err != nil {
		if err == sql.ErrNoRows {
			return fail(fmt.Errorf("no such album"))
		}
		return fail(err)
	}
	if !enough {
		return fail(fmt.Errorf("not enough inventory"))
	}

	// Update the album inventory to remove the quantity in the order.
	_, err = tx.ExecContext(ctx, "UPDATE album SET quantity = quantity - ? WHERE id = ?",
		quantity, albumID)
	if err != nil {
		return fail(err)
	}

	// Create a new row in the album_order table.
	result, err := tx.ExecContext(ctx, "INSERT INTO album_order (album_id, cust_id, quantity, date) VALUES (?, ?, ?, ?)",
		albumID, custID, quantity, time.Now())
	if err != nil {
		return fail(err)
	}
	// Get the ID of the order item just created.
	orderID, err = result.LastInsertId()
	if err != nil {
		return fail(err)
	}

	// Commit the transaction.
	if err = tx.Commit(); err != nil {
		return fail(err)
	}

	// Return the order ID.
	return orderID, 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.