Golang连接mysql执行事务

下面是一个使用事务的例子:

  • 创建一个订单,如果库存够,则更新album表里面库存数量,然后往album_order里插入新行,提交事务。最后返回一个订单ID。
  • 如果过程中有失败,则回滚。
// createOrder creates an order for an album and return the new order ID.
func createOrder(ctx context.Context, albumID, quantity, customID int) (int64, error) {
	// Create a helper function for preparing failure result.
	fail := func(err error) (int64, error) {
		return 0, fmt.Errorf("createOrder: %v", err)
	}

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

	// Confirm the 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 {
			fail(fmt.Errorf("no such album"))
		}
		return fail(err)
	}
	if !enough {
		fail(fmt.Errorf("no 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 {
		fail(err)
	}

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

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

	// Return the order ID.
	return orderID, nil
}