go標準庫的學習-database/sql/driver

參考:https://studygolang.com/pkgdocgolang

1》導入方式:sql

import "database/sql/driver"

driver包定義了應被數據庫驅動實現的接口,這些接口會被sql包使用。 數據庫

絕大多數代碼應使用sql包。緩存

 

2》driver.Driver - 在database/sql/driver中函數

Driver是一個數據庫驅動的接口,其定義了一個Open(name string)方法,該方法返回一個數據庫的Conn接口:ui

type Driver interface { // Open返回一個新的與數據庫的鏈接,參數name的格式是驅動特定的。 // // Open可能返回一個緩存的鏈接(以前關閉的鏈接),但這麼作是沒必要要的; // sql包會維護閒置鏈接池以便有效的重用鏈接。 // // 返回的鏈接同一時間只會被一個go程使用。 Open(name string) (Conn, error) }

由於返回的鏈接同一時間只會被一個go程使用,因此返回的Conn只能用來進行一次goroutine操做,即不能把這個Conn應用於Go的多個goroutine中,不然會出現錯誤,如:
spa

go goroutineA(Conn) //執行查詢操做 go goroutineB(Conn) //執行插入操做

這樣的代碼會使Go不知某個操做究竟是由哪一個goroutine發起的從而致使數據混亂。便可能會講goroutineA裏面執行的查詢操做的結果返回給goroutineB,從而讓goroutineB將此結果當成本身執行的插入數據
code

 

 

3》driver.Conn - 在database/sql/driver中blog

Conn是一個數據鏈接的接口定義。這個Conn只能應用在一個goroutine中,如上所說。接口

type Conn interface { // Prepare返回一個準備好的、綁定到該鏈接的狀態。 Prepare(query string) (Stmt, error) // Close做廢並中止任何如今準備好的狀態和事務,將該鏈接標註爲再也不使用。 // // 由於sql包維護着一個鏈接池,只有當閒置鏈接過剩時纔會調用Close方法, // 驅動的實現中不須要添加本身的鏈接緩存池。  Close() error // Begin開始並返回一個新的事務。  Begin() (Tx, error) }

Prepare函數返回與當前鏈接相關的SQL語句的準備狀態,能夠進行查詢、刪除等操做

Close函數關閉當前的鏈接,執行釋放鏈接擁有的資源等清理工做。由於驅動實現了database/sql中建議的conn pool,因此不用再去實現緩存conn之類的,這樣會更容易引發問題

Begin函數返回一個表明事務處理的Tx,經過它你能夠進行查詢、更新等操做,或者對事務進行回滾、遞交

 

4》driver.Stmt - 在database/sql/driver中

Stmt是一種準備好的狀態,綁定到一個Conn中,並只能應用在一個goroutine中。

type Stmt interface { // Close關閉Stmt。 // // 和Go1.1同樣,若是Stmt被任何查詢使用中的話,將不會被關閉。  Close() error // NumInput返回佔位參數的個數。 // // 若是NumInput返回值 >= 0,sql包會提早檢查調用者提供的參數個數, // 而且會在調用Exec或Query方法前返回數目不對的錯誤。 // // NumInput能夠返回-1,若是驅動佔位參數的數量不知時。 // 此時sql包不會提早檢查參數個數。 NumInput() int // Exec執行查詢,而不會返回結果,如insert或update。  Exec(args []Value) (Result, error) // Query執行查詢並返回結果,如select。  Query(args []Value) (Rows, error) }

Close函數關閉當前的鏈接狀態,可是若是當前正在執行query,query仍是會有效地返回rows數據

Exec函數執行Conn的Prepare準備好的sql,傳入參數執行update/insert等操做,返回Result數據

Query函數執行Conn的Prepare準備好的sql,傳入須要的參數執行select操做,返回Rows結果集

 

5》driver.Tx - 在database/sql/driver中

事務處理通常就兩個過程,遞交或回滾,即下面的兩個函數:

type Tx interface { Commit() error Rollback() error }

 

6》driver.Execer - 在database/sql/driver中

這是一個Conn可選擇實現的接口

type Execer interface { Exec(query string, args []Value) (Result, error) }

若是一個Conn未實現Execer接口,sql包的DB.Exec會首先準備一個查詢(即調用Prepare返回Stmt),執行狀態(即執行Stmt的Exec函數),而後關閉狀態(即關閉Stmt)。Exec可能會返回ErrSkip。

 

7》driver.Result

這是是執行Update/insert等操做返回的結果接口定義

type Result interface { // LastInsertId返回insert等命令後數據庫自動生成的ID  LastInsertId() (int64, error) // RowsAffected返回被查詢影響的行數  RowsAffected() (int64, error) }

 

8》driver.Rows

Rows是執行查詢返回的結果集接口定義

type Rows interface { // Columns返回各列的名稱,列的數量能夠從切片長度肯定。 // 若是某個列的名稱未知,對應的條目應爲空字符串。 Columns() []string // Close關閉Rows。  Close() error // 調用Next方法以將下一行數據填充進提供的切片中,即返回下一條數據,並把數據返回給dest。 // 提供的切片必須和Columns返回的切片長度相同。 // // 切片dest可能被填充同一種驅動Value類型,但字符串除外;即dest裏面的元素必須是driver.Vlaue的值,除了string。 // 全部string值都必須轉換爲[]byte。 // // 當沒有更多行時,Next應返回io.EOF。  Next(dest []Value) error }

Columns函數返回查詢數據庫表的字段信息,返回的slice和sql查詢的字段一一對應,而不是返回整個表的全部字段

 

9》driver.RowsAffected

type RowsAffected int64

RowsAffected其實就是int64的別名,可是它實現了Result接口,用來底層實現Result的表示方式

RowsAffected實現了Result接口,用於insert或update操做,這些操做會修改零到多行數據。

func (RowsAffected) LastInsertId

func (RowsAffected) LastInsertId() (int64, error)

func (RowsAffected) RowsAffected

func (v RowsAffected) RowsAffected() (int64, error)


10》driver.Value

type Value interface{}

Value其實就是一個空接口,它能夠容納任何數據

driver.Value是驅動必須可以操做的Value,因此Value要麼是nil,要麼是下面的任意一種:

int64
float64
bool []byte string [*] Rows.Next不會返回該類型值 time.Time

 

11》driver.ValueConverter

ValueConverter接口定義了一個如何把一個普通值轉化成driver.Value的接口

type ValueConverter interface { // ConvertValue將一個值轉換爲驅動支持的Value類型 ConvertValue(v interface{}) (Value, error) }

ValueConverter接口提供了ConvertValue方法。

driver包提供了各類ValueConverter接口的實現,以保證不一樣驅動之間的實現和轉換的一致性。ValueConverter接口有以下用途:

  • 轉換sql包提供的driver.Value類型值到數據庫指定列的類型,並保證它的匹配,例如保證某個int64值知足一個表的uint16列。
  • 轉換數據庫提供的值(即數據庫查詢結果)成driver.Value類型。
  • 在Scan函數中被sql包用於將driver.Value類型轉換爲用戶定義的類型。

 

12》driver.Valuer

type Valuer interface { // Value返回一個驅動支持的Value類型值  Value() (Value, error) }

Valuer接口定義了一個返回driver.Value的方法

不少類型都實現了這個Value方法,用來實現自身與driver.Value的轉換

 

13》ColumnConverter

type ColumnConverter

type ColumnConverter interface {
    // ColumnConverter返回指定列的ValueConverter
    // 若是該列未指定類型,或不該特殊處理,應返回DefaultValueConverter
    ColumnConverter(idx int) ValueConverter
}

若是Stmt有本身的列類型,能夠實現ColumnConverter接口,返回值能夠將任意類型轉換爲驅動的Value類型。

 

14》變量

1.Bool

var Bool boolType

Bool是ValueConverter接口值,用於將輸入的值轉換爲布爾類型。使用方式爲driver.Bool(1),會返回true

轉換規則以下:

- 布爾類型:不作修改
- 整數類型:
     1 爲真
     0 爲假
     其他整數會致使錯誤
- 字符串和[]byte:與strconv.ParseBool的規則相同
- 全部其餘類型都會致使錯誤

其實現源碼爲:

var Bool boolType

type boolType struct{}

var _ ValueConverter = boolType{}

func (boolType) String() string { return "Bool" }

func (boolType) ConvertValue(src interface{}) (Value, error) {
    switch s := src.(type) { //首先查看輸入的src值的類型
    case bool: //若是輸入的是bool類型,則直接輸出便可
        return s, nil
    case string: //若是是其餘類型,則要將其轉成Bool類型
        b, err := strconv.ParseBool(s)
        if err != nil {
            return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s)
        }
        return b, nil
    case []byte:
        b, err := strconv.ParseBool(string(s))
        if err != nil {
            return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s)
        }
        return b, nil
    }

    //除了上面的幾種類型,若是是下面的類型,只有當其值爲1或0時可以將其轉成bool類型
    sv := reflect.ValueOf(src)
    switch sv.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        iv := sv.Int()
        if iv == 1 || iv == 0 {
            return iv == 1, nil
        }
        return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", iv)
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        uv := sv.Uint()
        if uv == 1 || uv == 0 {
            return uv == 1, nil
        }
        return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", uv)
    }
    //除了上面的類型,若是輸入的src是其餘的類型,則會報錯
    return nil, fmt.Errorf("sql/driver: couldn't convert %v (%T) into type bool", src, src)
}

 

2.Int32

var Int32 int32Type

Int32是一個ValueConverter接口值,用於將值轉換爲int64類型,會尊重int32類型的限制。

3.String

var String stringType

String是一個ValueConverter接口值,用於將值轉換爲字符串。若是值v是字符串或者[]byte類型,不會作修改,若是值v是其它類型,會轉換爲fmt.Sprintf("%v", v)。

4.DefaultParameterConverter

var DefaultParameterConverter defaultConverter

DefaultParameterConverter是ValueConverter接口的默認實現,當一個Stmt沒有實現ColumnConverter時,就會使用它。 

若是值value知足函數IsValue(value)爲真,DefaultParameterConverter直接返回 value。不然,整數類型會被轉換爲int64,浮點數轉換爲float64,字符串轉換爲[]byte。其它類型會致使錯誤。

5.ResultNoRows

var ResultNoRows noRows

ResultNoRows是預約義的Result類型值,用於當一個DDL命令(如create table)成功時被驅動返回。它的LastInsertId和RowsAffected方法都返回錯誤

舉例:

package main 
import(
    "fmt"
    "reflect"
    "time"
    "database/sql/driver"
)

type valueConverterTest struct {
    c   driver.ValueConverter
    in  interface{}
    out interface{}
    err string
}

var now = time.Now()
var answer int64 = 42

type (
    i  int64
    f  float64
    b  bool
    bs []byte
    s  string
    t  time.Time
    is []int
)

var valueConverterTests = []valueConverterTest{
    {driver.Bool, "true", true, ""},
    {driver.Bool, "True", true, ""},
    {driver.Bool, []byte("t"), true, ""},
    {driver.Bool, true, true, ""},
    {driver.Bool, "1", true, ""},
    {driver.Bool, 1, true, ""},
    {driver.Bool, int64(1), true, ""},
    {driver.Bool, uint16(1), true, ""},
    {driver.Bool, "false", false, ""},
    {driver.Bool, false, false, ""},
    {driver.Bool, "0", false, ""},
    {driver.Bool, 0, false, ""},
    {driver.Bool, int64(0), false, ""},
    {driver.Bool, uint16(0), false, ""},
    {c: driver.Bool, in: "foo", err: "sql/driver: couldn't convert \"foo\" into type bool"},
    {c: driver.Bool, in: 2, err: "sql/driver: couldn't convert 2 into type bool"},
    {driver.DefaultParameterConverter, now, now, ""},
    {driver.DefaultParameterConverter, (*int64)(nil), nil, ""},
    {driver.DefaultParameterConverter, &answer, answer, ""},
    {driver.DefaultParameterConverter, &now, now, ""},
    {driver.DefaultParameterConverter, i(9), int64(9), ""},
    {driver.DefaultParameterConverter, f(0.1), float64(0.1), ""},
    {driver.DefaultParameterConverter, b(true), true, ""},
    {driver.DefaultParameterConverter, bs{1}, []byte{1}, ""},
    {driver.DefaultParameterConverter, s("a"), "a", ""},
    {driver.DefaultParameterConverter, is{1}, nil, "unsupported type main.is, a slice of int"},
}



func main() {
    for i, tt := range valueConverterTests {
        out, err := tt.c.ConvertValue(tt.in)
        goterr := ""
        if err != nil {
            goterr = err.Error()
        }
        if goterr != tt.err {
            fmt.Printf("test %d: %T(%T(%v)) error = %q; want error = %q\n",
                i, tt.c, tt.in, tt.in, goterr, tt.err)
        }
        if tt.err != "" {
            continue
        }
        if !reflect.DeepEqual(out, tt.out) {
            fmt.Printf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)\n",
                i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
        }
    }
}

 

6.錯誤ErrBadConn

var ErrBadConn = errors.New("driver: bad connection")

ErrBadConn應被驅動返回,以通知sql包一個driver.Conn處於損壞狀態(如服務端以前關閉了鏈接),sql包會重啓一個新的鏈接。

爲了不重複的操做,若是數據庫服務端執行了操做,就不該返回ErrBadConn。即便服務端返回了一個錯誤。

7.錯誤ErrSkip

var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented")

ErrSkip可能會被某些可選接口的方法返回,用於在運行時代表快速方法不可用,sql包應像未實現該接口的狀況同樣執行。ErrSkip只有文檔顯式說明的地方纔支持,如driver.Execer。

 

15》其餘函數

func IsValue

func IsValue(v interface{}) bool

IsValue報告v是不是合法的Value類型參數。和IsScanValue不一樣,IsValue接受字符串類型。

func IsScanValue

func IsScanValue(v interface{}) bool

IsScanValue報告v是不是合法的Value掃描類型參數。和IsValue不一樣,IsScanValue不接受字符串類型。

相關文章
相關標籤/搜索