Go Mysql驅動


 

Golang中MYSQL驅動

  • Mysql庫https://github.com/go-sql-driver/mysql
  • Go本身不提供具體數據庫驅動,只提供驅動接口和管理。
  • 各個數據庫驅動需要第三方實現,並且注冊到Go中的驅動管理中。

 

安裝golang mysql drvier

go get github.com/go-sql-driver/mysql

代碼中需要注冊mysql數據庫驅動,通過引入空白導入mysql包來完成。

為什么需要使用空白導入? 是因為需要執行mysql包的初始化代碼(代碼位於%GOPATH%/github.com/go-sql-driver/mysql/driver.go)

func init() {
    sql.Register("mysql", &MySQLDriver{})
}

 

連接數據的DSN格式

username:password@protocol(address)/dbname?param=value

 

Prepared Statement 
sql.Stmt支持預備表達式,可以用來優化SQL查詢提高性能,減少SQL注入的風險, DB.Prepare()和Tx.Prepare()都提供了對於預備表達式的支持。

預處理的流程:
step1. 將sql分為2部分.命令部分和數據部分.
step2. 首先將命令部分發送給mysql服務器,mysql進行預處理.(如生成AST)
step3. 然后將數據部分發送給mysql服務器,mysql進行占位符替換.
step4. mysql服務器執行sql語句,把執行結果發送給客戶端.

預處理的優勢:
1.因為發送命令后,在mysql服務器端,就會將AST生成好,所以不需要對每一次值的更換都重新生成一次AST.對同樣的數據不同的SQL來講,只需生成1次AST,並緩存起來即可.
2.避免SQL注入.因為mysql知道再次發送過來的內容為”數據”,因此不會將這些數據解析為SQL,避免了SQL注入.

需要注意的點:
使用預處理進行查詢操作時,不僅在defer時需要關閉結果集,而且還要關閉命令句柄,否則同樣會占用連接,導致阻塞.

package main

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

type User struct {
    Id int `db:"id"`
    Name string `db:"name"`
    Age int `db:"age"`
}


func PrepareQuery(db *sql.DB, id int) {
    stmt, err := db.Prepare("select id, name, age from user where id>?")
    if err != nil {
        panic(err)
    }

    rows, err := stmt.Query(id)
    if err != nil {
        panic(err)
    }

    defer stmt.Close()
    defer rows.Close()

    for rows.Next(){
        var user User
        err := rows.Scan(&user.Id, &user.Name, &user.Age)
        if err != nil {
            panic(err)
        }
        fmt.Printf("user: %#v\n", user)
    }
}

func main() {

    dns := "root:123456@tcp(172.16.65.200:3306)/golang"
    db, err := sql.Open("mysql", dns)
    if err != nil {
        panic(err)
    }

    defer db.Close()

    PrepareQuery(db, 0)

}
View Code

 

 

Mysql創建表:

  CREATE TABLE user (

  id int(20) NOT NULL AUTO_INCREMENT,

  name varchar(20) DEFAULT '',

  age int(2) DEFAULT '0',

  PRIMARY KEY (id))

  ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT  CHARSET=utf8mb4;

 

數據庫增刪改查

insert

package main

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

func Insert(db *sql.DB) {
    name := "Vincent"
    age := 18

    result, err := db.Exec("insert into user(name, age) values (?,?)", name, age)
    if err != nil {
        panic(err)
    }

    id, err := result.LastInsertId()
    if err != nil {
        panic(err)
    }

    affected, err := result.RowsAffected()
    if err != nil {
        panic(err)
    }

    fmt.Printf("last insert id:%d affect rows:%d\n", id, affected)
}

func main() {
    dns := "root:123456@tcp(172.16.65.200:3306)/golang"
    db, err := sql.Open("mysql", dns)
    if err != nil {
        panic(err)
    }

    err = db.Ping()
    if err != nil {
        panic(err)
    }
    fmt.Println("connect to db success!!!")
    Insert(db)
}
View Code

delete

package main

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

func Delete(db *sql.DB, id int) {
    result, err := db.Exec("delete from user where id=?", id)
    if err != nil {
        panic(err)
    }

    rowsAffected, err := result.RowsAffected()
    if err != nil {
        panic(err)
    }
    fmt.Printf("delect id:%d, affect rows:%d\n", id, rowsAffected)
}

func main() {
    dns := "root:123456@tcp(172.16.65.200:3306)/golang"
    db, _ := sql.Open("mysql", dns)
    Delete(db, 2)
}
View Code

update

package main

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

func Update(db *sql.DB) {
    name := "Miles"
    age := 88
    id := 3

    result, err := db.Exec("update user set name=?, age=? where id=?", name, age, id)
    if err != nil {
        panic(err)
    }

    // RowsAffected returns the number of rows affected by an
    // update, insert, or delete.
    rowsAffected, err := result.RowsAffected()
    if err != nil {
        panic(err)
    }

    fmt.Printf("update id:%d, affect rows:%d\n", id, rowsAffected)

}

func main() {
    dns := "root:123456@tcp(172.16.65.200:3306)/golang"
    db, err := sql.Open("mysql", dns)
    if err != nil {
        panic(err)
    }

    err = db.Ping()
    if err != nil {
        panic(err)
    }
    fmt.Println("connect to db success!!!")
    Update(db)
}
View Code

query

package main

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

type User struct {
    Id int `db:"id"`
    Name string `db:"name"`
    Age int `db:"age"`
}



// 單行查詢,如果查詢到多個結果,只返回第一行,查詢不到結果就ErrNoRows錯誤。
func QueryRow(db *sql.DB) {
    id:= 2
    row := db.QueryRow("select id, name, age from user where id=?", id)

    var user User
    err := row.Scan(&user.Id, &user.Name, &user.Age)

    if err == sql.ErrNoRows {
        log.Printf("not found data of the id:%d",id)
    }

    if err != nil {
        panic(err)
    }


    fmt.Printf("user: %#v\n", user)
}


// 多行查詢, 查詢不到任何記錄也不會報錯。
func Query(db *sql.DB) {
    id := 0
    rows, err := db.Query("select id, name, age from user where id>?", id)
    if err != nil {
        panic(err)
    }
    if err == sql.ErrNoRows {
        log.Printf("not found data of id:%d\n", id)
        return
    }
    defer rows.Close()

    for rows.Next() {
        var user User
        err := rows.Scan(&user.Id, &user.Name, &user.Age)
        if err != nil {
            panic(err)
        }
        fmt.Printf("user: %#v\n", user)
    }

}

func main() {
    dns := "root:123456@tcp(172.16.65.200:3306)/golang"
    db, err := sql.Open("mysql", dns)
    if err != nil {
        panic(err)
    }

    err = db.Ping()
    if err != nil {
        panic(err)
    }

    fmt.Printf("connect to db success\n")

    //QueryRow(db)

    Query(db)
}
View Code

 

事務支持 

事務(transaction)

  • transaction, err := Db.Begin() 開啟事務
  • transaction.Exec() 執行事務
  • transaction.Commit() 提交事務
  • transaction.Rollback() 回滾事務

A. 事務的應用場景
  1. 同時更新多個表
  2. 同時更新多行數據
B. 事務的ACID
  1. 原子性
  2. 一致性
  3. 隔離性
  4. 持久性

 

需要注意的點:
1. 執行失敗要回滾
2. 提交失敗要回滾

package main

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


func Transaction(db *sql.DB) {

    // 開啟事務
    tx, err := db.Begin()

    if err != nil {
        panic(err)
    }

    result, err := tx.Exec("insert into user(name, age)values(?,?)", "Jack", 98)
    if err != nil {
        // 失敗回滾
        tx.Rollback()
        panic(err)
    }
    
    fmt.Println("result", result)

    exec, err := tx.Exec("update user set name=?, age=? where id=?", "Jack", 98, 1)
    if err != nil {
        // 失敗回滾
        tx.Rollback()
        panic(err)
    }
    fmt.Println("exec", exec)

    // 提交事務
    err = tx.Commit()
    
    if err != nil {
        // 失敗回滾
        tx.Rollback()
        panic(err)
    }
}

func main() {

    dns := "root:123456@tcp(172.16.65.200:3306)/golang"
    db, err := sql.Open("mysql", dns)
    if err != nil {
        panic(err)
    }

    err = db.Ping()
    if err != nil {
        panic(err)
    }

    Transaction(db)
}
View Code

 

Mysql日期時間類型報錯

sql: Scan error on column index 1: unsupported Scan, storing driver.Value type []uint8 into type *time.Time

原因是在調用sql.Open()時沒有將parseTime設置為True。加入parseTime即可修復問題:

db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname?parseTime=true")

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM