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