Go使用mysql與事務操作
尚硅谷Golang課

Go使用mysql與事務操作

基本操作

import (
	"database/sql"
	"fmt"

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

var db *sql.DB // 是一個連接池對象

// user 接收數據
type user struct {
	id   int
	name string
	age  int
}

func initDB() (err error) {
	dsn := "root:root@tcp(127.0.0.1:3306)/sql_test"
	db, err = sql.Open("mysql", dsn) // 這個db要用全局的,重要!
	if err != nil {
		return
	}
	err = db.Ping()
	if err != nil {
		return
	} else {
		fmt.Println("連接成功")
	}
	return
}

// 查詢一個
func find1() {
	var u1 user
	sqlStr := `select id, name, age from user where id=?;`
	rowObj := db.QueryRow(sqlStr, 1)
	err := rowObj.Scan(&u1.id, &u1.name, &u1.age)
	if err != nil {
		fmt.Println("Scan failed!", err)
		return
	}
	fmt.Println(u1)
}

// findMore 查詢多個
func findMore() {
	sqlStr := `select id, name, age from user where id > ?;`
	rows, _ := db.Query(sqlStr, 0)
	defer rows.Close()

	for rows.Next() {
		var u user
		err := rows.Scan(&u.id, &u.name, &u.age)
		if err != nil {
			return
		}
		fmt.Println(u)
	}
}

func update(newAge int, id int) {
	sqlStr := "update user set age=? where id = ?"
	ret, err := db.Exec(sqlStr, newAge, id)
	if err != nil {
		fmt.Println(err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		return
	}
	fmt.Println("更新了", n, "行數據")
}

func delete(id int) {
	sqlStr := "delete from user where id = ?"
	ret, err := db.Exec(sqlStr, id)
	if err != nil {
		fmt.Println(err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		return
	}
	fmt.Println("更新了", n, "行數據")
}

func main() {
	err := initDB()
	if err != nil {
		fmt.Println("init DB failed", err)
	}

	preInsert()
	// findMore()
	// update(90, 2)
	// delete(2)
	findMore()
}

func insert() {
	sqlStr := `insert into user(name, age) values("AAC", 18)`
	ret, err := db.Exec(sqlStr)
	if err != nil {
		fmt.Println("insert failed!", err)
		return
	}
	// 插入後能拿到對應的ID
	id, _ := ret.LastInsertId()
	fmt.Println("id=", id)
}

func preInsert() {
	sqlStr := "insert into user(name, age) values(?,?)"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		return
	}
	defer stmt.Close()
	for i := 1; i < 10; i++ {
		_, _ = stmt.Exec("人物", i)

	}
}

mysql事務操作

事務:一個最小的不可再分的工作單元;通常一個事務對應一個完整的業務(例如銀行帳戶轉帳業務,該業務就是一個最小的工作單元),同時這個完整的業務需要執行多次的DML(insert、update、delete)語句共同聯合完成。A轉帳給B,這裡面就需要執行兩次update操作。在MySQL中只有使用了Innodb資料庫引擎的資料庫或表才支援事務。交易處理可以用來維護資料庫的完整性,保證成批的SQL語句要麼全部執行,要麼全部不執行。

事務的ACID

通常事務必須滿足4個條件(ACID):原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、持久性(Durability)。

  1. 原子性 一個事務(transaction)中的所有操作,要麼全部完成,要麼全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
  2. 一致性 在事務開始之前和事務結束以後,資料庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及後續資料庫可以自發性地完成預定的工作。
  3. 隔離性 資料庫允許多個併發事務同時對其資料進行讀寫和修改的能力,隔離性可以防止多個事務併發執行時由於交叉執行而導致資料的不一致。事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和序列化(Serializable)。
  4. 持久性 交易處理結束後,對資料的修改就是永久的,即便系統故障也不會丟失。
import (
	"database/sql"
	"fmt"

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

var db *sql.DB // 是一個連接池對象

// user 接收數據
type user struct {
	id   int
	name string
	age  int
}

func initDB() (err error) {
	dsn := "root:root@tcp(127.0.0.1:3306)/sql_test"
	db, err = sql.Open("mysql", dsn) // 這個db要用全局的,重要!
	if err != nil {
		return
	}
	err = db.Ping()
	if err != nil {
		return
	} else {
		fmt.Println("連接成功")
	}
	return
}

func transaction() {
	tx, err := db.Begin()
	if err != nil {
		fmt.Println("begin failed!", err)
		return
	}

	sqlStr1 := "update user set age=age-2 where id=1"
	sqlStr2 := "update user set age=age+2 where id=3"
	ret1, err := tx.Exec(sqlStr1)
	if err != nil {
		tx.Rollback()
		return
	}
	affRow1, err := ret1.RowsAffected()
	if err != nil {
		tx.Rollback()
		return
	}
	ret2, err := tx.Exec(sqlStr2)
	if err != nil {
		tx.Rollback()
		return
	}
	affRow2, err := ret2.RowsAffected()
	if err != nil {
		tx.Rollback()
		return
	}
	fmt.Println(affRow1, affRow2)
	if affRow1 == 1 && affRow2 == 1 {
		tx.Commit()
		fmt.Println("已提交")
	} else {
		tx.Rollback()
	}

}

func findMore() {
	sqlStr := `select id, name, age from user where id > ?;`
	rows, _ := db.Query(sqlStr, 0)
	defer rows.Close()

	for rows.Next() {
		var u user
		err := rows.Scan(&u.id, &u.name, &u.age)
		if err != nil {
			return
		}
		fmt.Println(u)
	}
}

func main() {

	initDB()
	findMore()
	transaction()
	findMore()
}

上次修改於 2021-08-01

此篇文章的評論功能已經停用。