Go語(yǔ)言官方?jīng)]有提供數(shù)據(jù)庫(kù)驅(qū)動(dòng),而是為開發(fā)數(shù)據(jù)庫(kù)驅(qū)動(dòng)定義了標(biāo)準(zhǔn)接口database/sql,開發(fā)者可以根據(jù)database/sql接口來(lái)開發(fā)相應(yīng)的數(shù)據(jù)庫(kù)驅(qū)動(dòng),只要是按照標(biāo)準(zhǔn)接口database/sql開發(fā)的代碼,以后需要遷移數(shù)據(jù)庫(kù)時(shí),不需要任何修改。
創(chuàng)新互聯(lián)專注于瀾滄企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站制作。瀾滄網(wǎng)站建設(shè)公司,為瀾滄等地區(qū)提供建站服務(wù)。全流程按需制作,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
sql.Register函數(shù)用來(lái)注冊(cè)數(shù)據(jù)庫(kù)驅(qū)動(dòng),第三方開發(fā)者開發(fā)數(shù)據(jù)庫(kù)驅(qū)動(dòng)時(shí),會(huì)在init函數(shù)內(nèi)調(diào)用sql.Register完成本驅(qū)動(dòng)的注冊(cè)。
func Register(name string, driver driver.Driver) {
driversMu.Lock()
defer driversMu.Unlock()
if driver == nil {
panic("sql: Register driver is nil")
}
if _, dup := drivers[name]; dup {
panic("sql: Register called twice for driver " + name)
}
drivers[name] = driver
}
Go-SQL-Driver/MySQL數(shù)據(jù)庫(kù)驅(qū)動(dòng)的實(shí)現(xiàn)如下:
func init() {
sql.Register("mysql", &MySQLDriver{})
}
第三方數(shù)據(jù)庫(kù)驅(qū)動(dòng)通常通過(guò)調(diào)用sql.Register函數(shù)來(lái)注冊(cè)自己的數(shù)據(jù)庫(kù)驅(qū)動(dòng)名稱以及相應(yīng)的driver實(shí)現(xiàn)。在database/sql內(nèi)部通過(guò)一個(gè)map來(lái)存儲(chǔ)用戶定義的相應(yīng)驅(qū)動(dòng)。var drivers = make(map[string]driver.Driver)
Driver是一個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)的接口,定義了一個(gè)Open(name string)方法,返回一個(gè)數(shù)據(jù)庫(kù)的Conn接口。
type Driver interface {
Open(name string) (Conn, error)
}
Conn只能用來(lái)進(jìn)行一次goroutine操作,即Conn應(yīng)用于多個(gè)goroutine。
第三方驅(qū)動(dòng)都會(huì)實(shí)現(xiàn)driver.Driver接口,Open方法會(huì)解析name參數(shù)來(lái)獲取相關(guān)數(shù)據(jù)庫(kù)的連接信息,解析完成后,使用此連接信息來(lái)初始化一個(gè)Conn并返回。
driver.Conn是一個(gè)數(shù)據(jù)庫(kù)連接的接口,定義了一系列方法,Conn只能應(yīng)用在一個(gè)goroutine中,不能使用在多個(gè)goroutine中。
type Conn interface {
Prepare(query string) (Stmt, error)
Close() error
Begin() (Tx, error)
}
Prepare函數(shù)返回與當(dāng)前連接相關(guān)的執(zhí)行Sql語(yǔ)句的準(zhǔn)備狀態(tài),可以進(jìn)行查詢、刪除等操作。
Close函數(shù)關(guān)閉當(dāng)前的連接,執(zhí)行釋放連接擁有的資源等清理工作。通常第三方數(shù)據(jù)庫(kù)驅(qū)動(dòng)實(shí)現(xiàn)了database/sql建議的連接池,開發(fā)者不必去實(shí)現(xiàn)緩存連接。
Begin函數(shù)返回一個(gè)代表事務(wù)處理的Tx,通過(guò)Tx可以進(jìn)行查詢、更新等操作或者對(duì)事務(wù)進(jìn)行回滾、遞交。
driver.Stmt是一種準(zhǔn)備好的狀態(tài),與Conn相關(guān)聯(lián),而且只能應(yīng)用于一個(gè)goroutine中,不能應(yīng)用于多個(gè)goroutine。
type Stmt interface {
Close() error
NumInput() int
Exec(args []Value) (Result, error)
Query(args []Value) (Rows, error)
}
Close函數(shù)關(guān)閉當(dāng)前的連接狀態(tài),但如果當(dāng)前正在執(zhí)行query,query還是有效返回rows數(shù)據(jù)。
NumInput函數(shù)返回當(dāng)前預(yù)留參數(shù)的個(gè)數(shù),當(dāng)返回>=0時(shí)數(shù)據(jù)庫(kù)驅(qū)動(dòng)就會(huì)智能檢查調(diào)用者的參數(shù)。當(dāng)數(shù)據(jù)庫(kù)驅(qū)動(dòng)包不知道預(yù)留參數(shù)的時(shí)候,返回-1。
Exec函數(shù)執(zhí)行Prepare準(zhǔn)備好的sql,傳入?yún)?shù)執(zhí)行update/insert等操作,返回Result數(shù)據(jù)
Query函數(shù)執(zhí)行Prepare準(zhǔn)備好的sql,傳入需要的參數(shù)執(zhí)行select操作,返回Rows結(jié)果集
driver.Tx是事務(wù)接口,包含Commit、Rollback方法,數(shù)據(jù)庫(kù)驅(qū)動(dòng)只需實(shí)現(xiàn)Commit、Rollback函數(shù)即可。
type Tx interface {
Commit() error
Rollback() error
}
Go-SQL-Driver/MySQL數(shù)據(jù)庫(kù)驅(qū)動(dòng)的實(shí)現(xiàn)如下:
type mysqlTx struct {
mc *mysqlConn
}
func (tx *mysqlTx) Commit() (err error) {
if tx.mc == nil || tx.mc.closed.IsSet() {
return ErrInvalidConn
}
err = tx.mc.exec("COMMIT")
tx.mc = nil
return
}
func (tx *mysqlTx) Rollback() (err error) {
if tx.mc == nil || tx.mc.closed.IsSet() {
return ErrInvalidConn
}
err = tx.mc.exec("ROLLBACK")
tx.mc = nil
return
}
driver.Execer是一個(gè)Conn可選擇實(shí)現(xiàn)的接口。
type Execer interface {
Exec(query string, args []Value) (Result, error)
}
如果第三方數(shù)據(jù)庫(kù)驅(qū)動(dòng)沒(méi)有實(shí)現(xiàn)driver.Execer接口,調(diào)用DB.Exec會(huì)首先調(diào)用Prepare返回Stmt,然后執(zhí)行Stmt的Exec,然后關(guān)閉Stmt。
driver.Result是執(zhí)行Update/Insert等操作返回的結(jié)果接口。
type Result interface {
LastInsertId() (int64, error)
RowsAffected() (int64, error)
}
LastInsertId函數(shù)返回由數(shù)據(jù)庫(kù)執(zhí)行插入操作得到的自增ID號(hào)。
RowsAffected函數(shù)返回query操作影響的數(shù)據(jù)條目數(shù)。
driver.Rows是執(zhí)行查詢返回的結(jié)果集接口。
type Rows interface {
Columns() []string
Close() error
Next(dest []Value) error
}
Columns函數(shù)返回查詢數(shù)據(jù)庫(kù)表的字段信息,這個(gè)返回的slice和sql查詢的字段一一對(duì)應(yīng),而不是返回整個(gè)表的所有字段。
Close函數(shù)用來(lái)關(guān)閉Rows迭代器。
Next函數(shù)用來(lái)返回下一條數(shù)據(jù),把數(shù)據(jù)賦值給dest。dest里面的元素必須是driver.Value的值除了string,返回的數(shù)據(jù)里面所有的string都必須要轉(zhuǎn)換成[]byte。如果最后沒(méi)數(shù)據(jù)了,Next函數(shù)最后返回io.EOF。
driver.RowsAffected是int64的別名,但實(shí)現(xiàn)了Result接口,用來(lái)底層實(shí)現(xiàn)Result的表示方式。
type RowsAffected int64
var _ Result = RowsAffected(0)
func (RowsAffected) LastInsertId() (int64, error) {
return 0, errors.New("no LastInsertId available")
}
func (v RowsAffected) RowsAffected() (int64, error) {
return int64(v), nil
}
driver.Value是空接口,是數(shù)據(jù)庫(kù)驅(qū)動(dòng)必須能夠操作的Value,Value可以是nil,int64,float64,bool,[]bytestring,time.Time。type Value interface{}
driver.ValueConverter接口定義了如何把一個(gè)普通的值轉(zhuǎn)化成driver.Value的接口
type ValueConverter interface {
// ConvertValue converts a value to a driver Value.
ConvertValue(v interface{}) (Value, error)
}
driver.ValueConverter接口實(shí)現(xiàn)如下:
type stringType struct{}
func (stringType) ConvertValue(v interface{}) (Value, error) {
switch v.(type) {
case string, []byte:
return v, nil
}
return fmt.Sprintf("%v", v), nil
}
數(shù)據(jù)庫(kù)驅(qū)動(dòng)開發(fā)中,ConvertValue方法用途廣泛:
(1)轉(zhuǎn)化driver.value到數(shù)據(jù)庫(kù)表相應(yīng)的字段,例如int64的數(shù)據(jù)如何轉(zhuǎn)化成數(shù)據(jù)庫(kù)表uint16字段。
(2)把數(shù)據(jù)庫(kù)查詢結(jié)果轉(zhuǎn)化成driver.Value值
(3)在scan函數(shù)里面如何把driver.Value值轉(zhuǎn)化成用戶定義的值
driver.Valuer接口定義一個(gè)返回driver.Value的方法。
type Valuer interface {
// Value returns a driver Value.
Value() (Value, error)
}
func Open(driverName, dataSourceName string) (*DB, error) {
driversMu.RLock()
driveri, ok := drivers[driverName]
driversMu.RUnlock()
if !ok {
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
if driverCtx, ok := driveri.(driver.DriverContext); ok {
connector, err := driverCtx.OpenConnector(dataSourceName)
if err != nil {
return nil, err
}
return OpenDB(connector), nil
}
return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil
}
Open函數(shù)返回DB對(duì)象,
type DB struct {
connector driver.Connector
numClosed uint64
mu sync.Mutex // protects following fields
freeConn []*driverConn
connRequests map[uint64]chan connRequest
nextRequest uint64 // Next key to use in connRequests.
numOpen int // number of opened and pending open connections
openerCh chan struct{}
resetterCh chan *driverConn
closed bool
dep map[finalCloser]depSet
lastPut map[*driverConn]string // stacktrace of last conn's put; debug only
maxIdle int // zero means defaultMaxIdleConns; negative means 0
maxOpen int // <= 0 means unlimited
maxLifetime time.Duration // maximum amount of time a connection may be reused
cleanerCh chan struct{}
stop func() // stop cancels the connection opener and the session resetter.
}
freeConn是簡(jiǎn)易的連接池。當(dāng)執(zhí)行db.prepare?->?db.prepareDC時(shí)會(huì)defer dc.releaseConn,然后調(diào)用db.putConn,把連接放入連接池,每次調(diào)用db.conn的時(shí)候會(huì)先判斷freeConn的長(zhǎng)度是否大于0,大于0說(shuō)明有可以復(fù)用的conn,如果不大于0,則創(chuàng)建一個(gè)conn,然后返回。