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


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

1》導入方式:

import "database/sql/driver"

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

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

 

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

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

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

因為返回的連接同一時間只會被一個go程使用,所以返回的Conn只能用來進行一次goroutine操作,即不能把這個Conn應用於Go的多個goroutine中,否則會出現錯誤,如:

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

這樣的代碼會使Go不知某個操作到底是由哪個goroutine發起的從而導致數據混亂。即可能會講goroutineA里面執行的查詢操作的結果返回給goroutineB,從而讓goroutineB將此結果當成自己執行的插入數據

 

 

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

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不接受字符串類型。


免責聲明!

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



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