Golang操做數據庫

基本概念

  • Open() – creates a DB
  • Close() - closes the DB
  • Query() - 查詢
  • QueryRow() -查詢行
  • Exec() -執行操做,update,insert,delete
  • Row - A row is not a hash map, but an abstraction of a cursor
  • Next()
  • Scan()

注意:DB並非指的一個connectionmysql

鏈接到數據庫

咱們以mysql爲例,使用github.com/go-sql-driver/mysql,首先咱們須要導入咱們須要的包git

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

注意咱們導入github.com/go-sql-driver/mysql 前面用了一個"_",_操做實際上是引入該包,而不直接使用包裏面的函數,而是調用了該包裏面的init函數,import的時候實際上是執行了該包裏面的init函數,初始化了裏面的變量,_操做只是說該包引入了,我只初始化裏面的 init函數和一些變量,可是每每這些init函數裏面是註冊本身包裏面的引擎,讓外部能夠方便的使用,就不少實現database/sql的包,在 init函數裏面都是調用了sql.Register(name string, driver driver.Driver)註冊本身,而後外部就可使用了。
咱們用Open()函數來打開一個database handlegithub

db, err := sql.Open("mysql", "user:password@tcp(ip:port)/database")

寫一個完整的:sql

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "log"
)
func main() {
    db, err := sql.Open("mysql", "user:password@tcp(ip:port)/database")
    if err != nil {
        log.Println(err)
    }
    
    //在這裏進行一些數據庫操做
    
    defer db.Close()
}

咱們在執行Open函數的時候,並不會去得到數據庫鏈接有效性,當執行數據庫操做的時候纔會去鏈接,當咱們須要在Open以後就知道鏈接的有效性的時候,能夠經過Ping()來進行數據庫

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

咱們一般習慣使用Close來關閉數據庫鏈接,可是sql.DB是被設計成長期有效的類型,咱們不該該頻繁的Open和Close,相反,咱們應該創建一個sql.DB,在程序須要進行數據庫操做的時候一直使用它,不要在一個方法裏面進行Open和Close,應該把sql.DB做爲參數傳遞給方法app

進行數據庫操做

增刪改操做

Exec()方法通常用於增刪改操做,這裏以增長爲例:tcp

stmt, err := db.Prepare("insert into user(name,age)values(?,?)")
if err != nil {
    log.Println(err)
}

rs, err := stmt.Exec("go-test", 12)
if err != nil {
    log.Println(err)
}
//咱們能夠得到插入的id
id, err := rs.LastInsertId()
//能夠得到影響行數
affect, err := rs.RowsAffected()

查詢操做

通常的查詢

var name string
    var age int
    rows, err := db.Query("select name,age from user where id = ? ", 1)
    if err != nil {
        fmt.Println(err)
    }
    defer rows.Close()

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

    err = rows.Err()
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println("name:", url, "age:", description)

咱們應該養成關閉rows的習慣,在任什麼時候候,都不要忘記rows.Close().哪怕這個rows在確實循環完以後,已經自動關閉掉了,咱們定義rows.Close()也是對咱們沒有壞處的,由於咱們沒法保證,rows是否會正常的循環完。函數

查詢單條記錄,

咱們使用db.QueryRow()google

var name string
    err = db.QueryRow("select name from user where id = ?", 222).Scan(&name)

沒有結果的時候會返回errurl

處理空值

咱們用一個name字段爲空的記錄來舉例

var name NullString
err := db.QueryRow("SELECT name FROM names WHERE id = ?", id).Scan(&name)
...
if name.Valid {
        // use name.String
} else {
        // value is NULL
}

在這種狀況下咱們一般使用NullString,可是有時候咱們並不關心值是否是Null,咱們只須要吧他當一個空字符串來對待就行。這時候咱們可使用[]byte(null byte[]能夠轉化爲空string) 或者 sql.RawBytes,

var col1, col2 []byte

for rows.Next() {
    // Scan the value to []byte
    err = rows.Scan(&col1, &col2)

    if err != nil {
        panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
    }

    // Use the string value
    fmt.Println(string(col1), string(col2))
}

使用*sql.RawBytes

package main

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

func main() {
    // Open database connection
    db, err := sql.Open("mysql", "user:password@/dbname")
    if err != nil {
        panic(err.Error())  // Just for example purpose. You should use proper error handling instead of panic
    }
    defer db.Close()

    // Execute the query
    rows, err := db.Query("SELECT * FROM table")
    if err != nil {
        panic(err.Error()) // proper error handling instead of panic in your app
    }

    // Get column names
    columns, err := rows.Columns()
    if err != nil {
        panic(err.Error()) // proper error handling instead of panic in your app
    }

    // Make a slice for the values
    values := make([]sql.RawBytes, len(columns))

    // rows.Scan wants '[]interface{}' as an argument, so we must copy the
    // references into such a slice
    // See http://code.google.com/p/go-wiki/wiki/InterfaceSlice for details
    scanArgs := make([]interface{}, len(values))
    for i := range values {
        scanArgs[i] = &values[i]
    }

    // Fetch rows
    for rows.Next() {
        // get RawBytes from data
        err = rows.Scan(scanArgs...)
        if err != nil {
            panic(err.Error()) // proper error handling instead of panic in your app
        }

        // Now do something with the data.
        // Here we just print each column as a string.
        var value string
        for i, col := range values {
            // Here we can check if the value is nil (NULL value)
            if col == nil {
                value = "NULL"
            } else {
                value = string(col)
            }
            fmt.Println(columns[i], ": ", value)
        }
        fmt.Println("-----------------------------------")
    }
    if err = rows.Err(); err != nil {
        panic(err.Error()) // proper error handling instead of panic in your app
    }
}

事務

使用db.Begin()來開啓一個事務, 經過Commit()和Rollback()方法來關閉。

tx := db.Begin()
    tx.Rollback()
    tx.Commit()

Exec, Query, QueryRow and Prepare 方法已經所有能夠在tx上面使用。使用方法和在*sql.DB是同樣的,事務必須以Commit()或者Rollback()結束

The Connection Pool

在database/sql中有一個很基本的鏈接池,你並無多大的控制權,僅僅能夠設置SetMaxIdleConns和SetMaxOpenConns,也就是最大空閒鏈接和最大鏈接數。

db.SetMaxIdleConns(n)
    db.SetMaxOpenConns(n)
相關文章
相關標籤/搜索