1. 程式人生 > 實用技巧 >Go語言操作MySQL資料庫

Go語言操作MySQL資料庫

7,Go操作MySQl

1.依賴下載

go get -u github.com/go-sql-driver/mysql
  • 通過函式,驅動mysql
func Open(driverName, dataSourceName string) (*DB error)
  • 示例,開啟一個SQL連線
package main

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

func main() {
  // user 使用者名稱
  // password 密碼
  // host IP
  // port 埠
  // dbName 庫名
	dsn := "user:password@tcp(host:port)/dbName"
	db, err := sql.Open("mysql",dsn)
	if err != nil{
		panic(err)
	}
	defer db.Close()// 這裡寫在err下面。
}

2.連線的初始化

  • Open函式可能只是驗證其參互格式是否正確,實際上並不建立與資料庫的連線,如果要檢查資料來源的名稱是否真實有效應該呼叫ping

  • 返回DB物件可以被多個goroutine併發使用,並且維護其自己的空閒連線池。因此Open函式應該僅被呼叫一次,很少需要關閉這個DB物件。

    package main
    
    import (
    	"database/sql"
    	"fmt"
    	_ "github.com/go-sql-driver/mysql"
    )
    // 全域性定義db物件
    var db *sql.DB
    
    func initDB() (err error) {
    	dsn := "root:123@tcp(IP:HOST)/user_list?charset=utf8mb4&parseTime=True"
    	// 注意!!!這裡不要使用:=,我們是給全域性變數賦值,然後在main函式中使用全域性變數db
    	db, err = sql.Open("mysql",dsn)
    	if err != nil{
    		return err
    	}
    	// 嘗試與資料庫建立連線(校驗dsn是否正確)
    	err = db.Ping()
    	if err != nil{
    		return err
    	}
    	return nil
    }
    
    func main() {
    	err := initDB()
    	if err != nil {
    		fmt.Printf("init db failed, err:%v\n", err)
    		return
    	}
    }
    
  • Sql.DB 表示連線資料庫物件(結構體例項)。它內部維護著一個具有零到多個底層連線的連線池,它可以安全地被多個goroutine同時使用。

3.SetMaxOpenConns

  • SetMaxOpenConns設定與資料庫建立連線的最大數目。如果n大雨0且小於最大閒置連線數,會將最大閒置連線數減小到匹配最大開啟連線數的限制。 如果n<=0,不會限制最大開啟連線數,預設為0(無限制)

    func (db *DB) SetMaxOpenConns(n int)
    

4.SetMaxIdleConns

  • SetMaxIdleConns設定連線池中的最大閒置連線數。 如果n大於最大開啟連線數,則新的最大閒置連線數會減小到匹配最大開啟連線數的限制。 如果n<=0,不會保留閒置連線。

    func (db *DB) SetMaxIdleConns(n int)
    

5.CURD boy

5.1 插入資料

  • 插入,更新和刪除操作使用Exec方法。

    func (db *DB) Exec(query string, args ...interface{}) (Result, error)
    
  • 操作:

    var db *sql.DB
    
    func inserRowDemo() {
    	sqlStr := "insert into user(name,age) values (?,?)"
    	ret, err := db.Exec(sqlStr,"小明",25)
    	if err != nil {
    		fmt.Printf("insert failed, err:%v\n", err)
    	}
    	// 獲取新資料插入id
    	theID, err := ret.LastInsertId()
    	if err != nil {
    		fmt.Printf("get last insert IO failed, err:%v\n", err)
    		return
    	}
    	fmt.Printf("insert success, the id is %d.\n",theID)
    }
    // insert success, the id is 1.
    

5.2 更新操作

func updateRowDemo() {
	sqlStr := "update user set age=? where id=?"
	ret,err := db.Exec(sqlStr,35,3)
	if err != nil{
		fmt.Printf("update failed, err:%v\n",err)
		return
	}
	n, err := ret.RowsAffected() //獲取操作影響的行數
	if err!= nil {
		fmt.Printf("get RowsAffected failed err:%v\n",err)
		return
	}
	fmt.Printf("update success affected rows:%d\n",n)
}
// update success affected rows:1

5.3 刪除操作

func deleteRowDemo() {
	sqlStr := "delete from user where id=?"
	ret, err := db.Exec(sqlStr,3)
	if err != nil {
		fmt.Printf("delete failed, err:%v\n", err)
		return
	}
	n, err := ret.RowsAffected() //操作影響的行數
	if err!=nil{
		fmt.Printf("get RowAffected failed, err:%v\n",err)
		return
	}
	fmt.Printf("delete success, affected rows:%d\n", n)
}

5.4單行資料查詢

  • 單行查詢通過:db.QueryRow().
func (db *DB) QueryRow(query string, args ...interface{}) *Row
  • 示例程式碼
type user struct {
	id   int
	age  int
	name string
}

func queryRowDemo(){
	sqlStr := "select id,name,age from user where id=?"
	var u user
	err := db.QueryRow(sqlStr, 1).Scan(&u.id,&u.name,&u.age)
	if err != nil{
		fmt.Printf("scan failed ,err:%v\n",err)
		return
	}
	fmt.Printf("id:%d name:%s age:%d",u.id,u.name,u.age)//id:1 name:小明 age:25
}

5.5多行查詢

  • 多行查詢db.Query() 執行一次查詢。返回多行結果。一般用於執行select命令,引數 args表示query中的佔位引數。
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
  • 示例程式碼
type user struct {
	id   int
	age  int
	name string
}

func queryMultiRowDemo(){
	sqlStr := "select id,name,age from user where id > ?"
	rows, err := db.Query(sqlStr,0)
	if err!=nil{
		fmt.Printf("query failed err:%v\n",err)
		return
	}
	// 關閉rows 釋放持有資料庫連結
	defer rows.Close()
	// 迴圈讀取結果集資料
	for rows.Next() {
		var u user
		err := rows.Scan(&u.id,&u.name,&u.age)
		if err != nil{
			fmt.Printf("scan failed err:%v\n", err)
			return
		}
		fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
	}
}

6.MySQL預處理

6.1什麼是預處理?

  • 普通SQL執行:

    • 客戶端對SQL語句進行佔位符替換得到完整的SQL語句。
    • 客戶端傳送完整SQL語句到MySQL服務端。
    • MySQL服務端執行完整的SQL語句並將結果返回給客戶端。
  • 預處理執行過程:

    • 把SQL語句分成兩部分,命令部分與資料部分。
    • 先把命令部分發送給MySQL服務端,MySQL服務端進行SQL預處理。
    • 然後把資料部分發送給MySQL服務端,MySQL服務端對SQL語句進行佔位替換。
    • MySQL服務端執行完整的SQL語句並將結果返回給客戶端。

6.2預處理好處

  • 避免SQL注入問題。
  • 優化MySQL伺服器重複執行SQL的方法,可以提升伺服器效能,提前讓伺服器編譯,一次編譯多次執行,節省後續編譯成本。

6.3預處理示例

  • 查詢操作的預處理示例程式碼如下:

    func prepareQueryDemo() {
    	sqlStr := "select id,name,age from user where id > ?"
    	// 預處理一條SQL語句
    	stmt, err := db.Prepare(sqlStr)
    	if err != nil {
    		fmt.Printf("prepare failed,err:%v\n", err)
    		return
    	}
    	defer stmt.Close()
    	// 資料部分發送MySQL服務端。
    	rows, err := stmt.Query(0)
    	if err != nil {
    		fmt.Printf("query failed err:%v\n",err)
    		return
    	}
    	defer rows.Close()
    	// 逐個讀取資料
    	for rows.Next() {
    		var u user
    		err := rows.Scan(&u.id, &u.name, &u.age)
    		if err != nil{
    			fmt.Printf("scan failed,err:%v\n", err)
    			return
    		}
    		fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
    	}
    }
    
  • 插入訊息預處理示例

    func preporeInsertDemo() {
    	sqlStr := "insert into user(name,age) values(?,?)"
    	// 預處理一條SQL語句
    	stmt, err := db.Prepare(sqlStr)
    	if err != nil {
    		fmt.Printf("prepare failed, err:%v\n", err)
    	}
    	defer stmt.Close()
    	// 新增資料
    	_, err = stmt.Exec("小紅", 18)
    	if err != nil {
    		fmt.Printf("insert failed, err:%v\n", err)
    		return
    	}
    	_, err = stmt.Exec("李二",25)
    	if err != nil {
    		fmt.Printf("insert failed err:%v\n", err)
    	}
    	fmt.Println("insert success!")
    }
    

6.4資料庫佔位符

資料庫 佔位符語法
MySQL ?
PostgreSQL $1, $2等
SQLite ?和$1
Oracle :name

7.Go語言實現MySQL事務

  • 事務具有四大特性:原子性,一致性,隔離性,永續性。

  • 事務相關語法:

    func (db *DB) Begin() (*Tx, error) // 開啟事務
    func (tx *Tx) Commit() error	//提交事務
    func (tx *Tx) Rollback() error //事務回滾
    
  • 事務示例

    // 事務示例
    func transactionDemo() {
    	tx, err := db.Begin() //開啟事務
    	if err != nil{
    		if tx != nil{
    			tx.Rollback()//回滾
    		}
    		fmt.Printf("begin trans failed, err:%v\n",err)
    		return
    	}
    	sqlStrl := "Update user set age=18 where id=?"
    	ret1, err := tx.Exec(sqlStrl,1)
    	if err != nil {
    		tx.Rollback()
    		fmt.Printf("exec sql1 failed, err:%v\n",err)
    		return
    	}
    	// 獲取更改行數
    	affRow1, err := ret1.RowsAffected()
    	if err != nil {
    		fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n",err)
    		return
    	}
    	sqlStr2 := "Update user set age=22 where id=?"
    	ret2, err := tx.Exec(sqlStr2, 4)
    	if err != nil{
    		tx.Rollback()//回滾
    		fmt.Printf("exec sql2 failed, err:%v\n", err)
    		return
    	}
    	affRow2, err := ret2.RowsAffected()
    	if err != nil {
    		tx.Rollback()
    		fmt.Printf("exec ret2.RowsAffected() failed,Err:%v\n", err)
    		return
    	}
    	fmt.Println(affRow1,affRow2)
    	if affRow1 == 1 && affRow2 == 1 {
    		fmt.Println("事務提交了...")
    		tx.Commit()//提交事務
    	} else {
    		tx.Rollback()
    		fmt.Println("事務回滾了...")
    	}
    	fmt.Println("exec trans success!")
    }
    
  • 參考部落格:https://www.liwenzhou.com/posts/Go/go_mysql/