天天看點

Golang如何優雅連接配接MYSQL資料庫?(下)

執行流程

使用db.Query()來發送查詢到資料庫,擷取結果集Rows,并檢查錯誤。

使用rows.Next()作為循環條件,疊代讀取結果集。

使用rows.Scan從結果集中擷取一行結果。

使用rows.Err()在退出疊代後檢查錯誤。

使用rows.Close()關閉結果集,釋放連接配接。

增删改和Exec

通常不會限制你查詢必須用Query,隻是Query會傳回結果集,而Exec不會傳回。是以如果你執行的是增删改操作一般用Exec會好一些。Exec傳回的結果是

Result

Result

接口允許擷取執行結果的中繼資料:

type Result interface {
    // 用于傳回自增ID,并不是所有的關系型資料庫都有這個功能。
    LastInsertId() (int64, error)
    // 傳回受影響的行數。
    RowsAffected() (int64, error)
}      

準備查詢

如果你現在想使用占位符的功能,where 的條件想以參數的形式傳入,Go提供了db.Prepare語句來幫你綁定。準備查詢的結果是一個準備好的語句(prepared statement),語句中可以包含執行時所需參數的占位符(即綁定值)。準備查詢比拼字元串的方式好很多,它可以轉義參數,避免SQL注入。同時,準備查詢對于一些資料庫也省去了解析和生成執行計劃的開銷,有利于性能。

占位符

PostgreSQL使用$N作為占位符,N是一個從1開始遞增的整數,代表參數的位置,友善參數的重複使用。MySQL使用?作為占位符,SQLite兩種占位符都可以,而Oracle則使用:param1的形式。

MySQL               PostgreSQL            Oracle
=====               ==========            ======
WHERE col = ?       WHERE col = $1        WHERE col = :col
VALUES(?, ?, ?)     VALUES($1, $2, $3)    VALUES(:val1, :val2, :val3)
stmt, e := DB.Prepare("select * from user where id=?")
query, e := stmt.Query(1)
query.Scan()      

5. 事務的使用

通過db.Begin()來開啟一個事務,Begin方法會傳回一個事務對象Tx。在結果變量Tx上調用Commit()或者Rollback()方法會送出或復原變更,并關閉事務。在底層,Tx會從連接配接池中獲得一個連接配接并在事務過程中保持對它的獨占。事務對象Tx上的方法與資料庫對象sql.DB的方法一一對應,例如Query,Exec等。事務對象也可以準備(prepare)查詢,由事務建立的準備語句會顯式綁定到建立它的事務。

//開啟事務
tx, err := DB.Begin()
if err != nil {
    fmt.Println("tx fail")
}
//準備sql語句
stmt, err := tx.Prepare("DELETE FROM user WHERE id = ?")
if err != nil {
    fmt.Println("Prepare fail")
    return false
}
//設定參數以及執行sql語句
res, err := stmt.Exec(user.id)
if err != nil {
    fmt.Println("Exec fail")
    return false
}
//送出事務
tx.Commit()
 
 
我們來一個完整的sql操作:package main
 
 
import (
    "database/sql"
    "encoding/json"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "github.com/pkg/errors"
    "strings"
)
 
 
//資料庫配置
const (
    userName = "root"
    password = "123456"
    ip       = "127.0.0.1"
    port     = "3306"
    dbName   = "test"
)
 
 
//Db資料庫連接配接池
var DB *sql.DB
 
 
type User struct {
    id    int64
    name  string
    age   int8
    sex   int8
    phone string
}
 
 
//注意方法名大寫,就是public
func InitDB() {
    //建構連接配接:"使用者名:密碼@tcp(IP:端口)/資料庫?charset=utf8"
    path := strings.Join([]string{userName, ":", password, "@tcp(", ip, ":", port, ")/", dbName, "?charset=utf8"}, "")
    //打開資料庫,前者是驅動名,是以要導入:_ "github.com/go-sql-driver/mysql"
    DB, _ = sql.Open("mysql", path)
    //設定資料庫最大連接配接數
    DB.SetConnMaxLifetime(100)
    //設定上資料庫最大閑置連接配接數
    DB.SetMaxIdleConns(10)
    //驗證連接配接
    if err := DB.Ping(); err != nil {
        fmt.Println("open database fail")
        return
    }
    fmt.Println("connnect success")
}
 
 
//查詢操作
func Query() {
    var user User
    rows, e := DB.Query("select * from user where id in (1,2,3)")
    if e == nil {
        errors.New("query incur error")
    }
    for rows.Next() {
        e := rows.Scan(user.sex, user.phone, user.name, user.id, user.age)
        if e != nil {
            fmt.Println(json.Marshal(user))
        }
    }
    rows.Close()
    DB.QueryRow("select * from user where id=1").Scan(user.age, user.id, user.name, user.phone, user.sex)
 
 
    stmt, e := DB.Prepare("select * from user where id=?")
    query, e := stmt.Query(1)
    query.Scan()
}
 
 
func DeleteUser(user User) bool {
    //開啟事務
    tx, err := DB.Begin()
    if err != nil {
        fmt.Println("tx fail")
    }
    //準備sql語句
    stmt, err := tx.Prepare("DELETE FROM user WHERE id = ?")
    if err != nil {
        fmt.Println("Prepare fail")
        return false
    }
    //設定參數以及執行sql語句
    res, err := stmt.Exec(user.id)
    if err != nil {
        fmt.Println("Exec fail")
        return false
    }
    //送出事務
    tx.Commit()
    //獲得上一個insert的id
    fmt.Println(res.LastInsertId())
    return true
}
 
 
func InsertUser(user User) bool {
    //開啟事務
    tx, err := DB.Begin()
    if err != nil {
        fmt.Println("tx fail")
        return false
    }
    //準備sql語句
    stmt, err := tx.Prepare("INSERT INTO user (`name`, `phone`) VALUES (?, ?)")
    if err != nil {
        fmt.Println("Prepare fail")
        return false
    }
    //将參數傳遞到sql語句中并且執行
    res, err := stmt.Exec(user.name, user.phone)
    if err != nil {
        fmt.Println("Exec fail")
        return false
    }
    //将事務送出
    tx.Commit()
    //獲得上一個插入自增的id
    fmt.Println(res.LastInsertId())
    return true
}
 
 
func main() {
    InitDB()
    Query()
    defer DB.Close()
}      

參考