Go语言开发(十七)、Go语言database/sql接口

栏目: Go · 发布时间: 6年前

内容简介:Go语言官方没有提供数据库驱动,而是为开发数据库驱动定义了标准接口database/sql,开发者可以根据database/sql接口来开发相应的数据库驱动,只要是按照标准接口database/sql开发的代码,以后需要迁移数据库时,不需要任何修改。sql.Register函数用来注册数据库驱动,第三方开发者开发数据库驱动时,会在init函数内调用sql.Register完成本驱动的注册。Go-SQL-Driver/MySQL数据库驱动的实现如下:

Go语言开发(十七)、 Go 语言database/sql接口

一、database/sql接口

Go语言官方没有提供数据库驱动,而是为开发数据库驱动定义了标准接口database/sql,开发者可以根据database/sql接口来开发相应的数据库驱动,只要是按照标准接口database/sql开发的代码,以后需要迁移数据库时,不需要任何修改。

二、database/sql常用接口

1、sql.Register

sql.Register函数用来注册数据库驱动,第三方开发者开发数据库驱动时,会在init函数内调用sql.Register完成本驱动的注册。

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数据库驱动的实现如下:

func init() {
   sql.Register("mysql", &MySQLDriver{})
}

第三方数据库驱动通常通过调用sql.Register函数来注册自己的数据库驱动名称以及相应的driver实现。在database/sql内部通过一个map来存储用户定义的相应驱动。

var drivers = make(map[string]driver.Driver)

2、driver.Driver

Driver是一个数据库驱动的接口,定义了一个Open(name string)方法,返回一个数据库的Conn接口。

type Driver interface {
      Open(name string) (Conn, error)
}

Conn只能用来进行一次goroutine操作,即Conn应用于多个goroutine。

第三方驱动都会实现driver.Driver接口,Open方法会解析name参数来获取相关数据库的连接信息,解析完成后,使用此连接信息来初始化一个Conn并返回。

3、driver.Conn

driver.Conn是一个数据库连接的接口,定义了一系列方法,Conn只能应用在一个goroutine中,不能使用在多个goroutine中。

type Conn interface {
   Prepare(query string) (Stmt, error)
   Close() error
   Begin() (Tx, error)
}

Prepare函数返回与当前连接相关的执行 Sql 语句的准备状态,可以进行查询、删除等操作。

Close函数关闭当前的连接,执行释放连接拥有的资源等清理工作。通常第三方数据库驱动实现了database/sql建议的连接池,开发者不必去实现缓存连接。

Begin函数返回一个代表事务处理的Tx,通过Tx可以进行查询、更新等操作或者对事务进行回滚、递交。

4、driver.Stmt

driver.Stmt是一种准备好的状态,与Conn相关联,而且只能应用于一个goroutine中,不能应用于多个goroutine。

type Stmt interface {
   Close() error

   NumInput() int
   Exec(args []Value) (Result, error)
   Query(args []Value) (Rows, error)
}

Close函数关闭当前的连接状态,但如果当前正在执行query,query还是有效返回rows数据。

NumInput函数返回当前预留参数的个数,当返回>=0时数据库驱动就会智能检查调用者的参数。当数据库驱动包不知道预留参数的时候,返回-1。

Exec函数执行Prepare准备好的sql,传入参数执行update/insert等操作,返回Result数据

Query函数执行Prepare准备好的sql,传入需要的参数执行select操作,返回Rows结果集

5、driver.Tx

driver.Tx是事务接口,包含Commit、Rollback方法,数据库驱动只需实现Commit、Rollback函数即可。

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

Go-SQL-Driver/MySQL数据库驱动的实现如下:

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
}

6、driver.Execer

driver.Execer是一个Conn可选择实现的接口。

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

如果第三方数据库驱动没有实现driver.Execer接口,调用DB.Exec会首先调用Prepare返回Stmt,然后执行Stmt的Exec,然后关闭Stmt。

7、driver.Result

driver.Result是执行Update/Insert等操作返回的结果接口。

type Result interface {
   LastInsertId() (int64, error)
   RowsAffected() (int64, error)
}

LastInsertId函数返回由数据库执行插入操作得到的自增ID号。

RowsAffected函数返回query操作影响的数据条目数。

8、driver.Rows

driver.Rows是执行查询返回的结果集接口。

type Rows interface {
   Columns() []string
   Close() error
   Next(dest []Value) error
}

Columns函数返回查询数据库表的字段信息,这个返回的slice和sql查询的字段一一对应,而不是返回整个表的所有字段。

Close函数用来关闭Rows迭代器。

Next函数用来返回下一条数据,把数据赋值给dest。dest里面的元素必须是driver.Value的值除了string,返回的数据里面所有的string都必须要转换成[]byte。如果最后没数据了,Next函数最后返回io.EOF。

9、driver.RowsAffected

driver.RowsAffected是int64的别名,但实现了Result接口,用来底层实现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
}

10、driver.Value

driver.Value是空接口,是数据库驱动必须能够操作的Value,Value可以是nil,int64,float64,bool,[]bytestring,time.Time。

type Value interface{}

11、driver.ValueConverter

driver.ValueConverter接口定义了如何把一个普通的值转化成driver.Value的接口

type ValueConverter interface {
   // ConvertValue converts a value to a driver Value.
   ConvertValue(v interface{}) (Value, error)
}

driver.ValueConverter接口实现如下:

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
}

数据库驱动开发中,ConvertValue方法用途广泛:

(1)转化driver.value到数据库表相应的字段,例如int64的数据如何转化成数据库表uint16字段。

(2)把数据库查询结果转化成driver.Value值

(3)在scan函数里面如何把driver.Value值转化成用户定义的值

12、driver.Valuer

driver.Valuer接口定义一个返回driver.Value的方法。

type Valuer interface {
   // Value returns a driver Value.
   Value() (Value, error)
}

13、sql.Open

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函数返回DB对象,

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是简易的连接池。当执行db.prepare -> db.prepareDC时会defer dc.releaseConn,然后调用db.putConn,把连接放入连接池,每次调用db.conn的时候会先判断freeConn的长度是否大于0,大于0说明有可以复用的conn,如果不大于0,则创建一个conn,然后返回。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Ruby

Programming Ruby

Dave Thomas、Chad Fowler、Andy Hunt / Pragmatic Bookshelf / 2004-10-8 / USD 44.95

Ruby is an increasingly popular, fully object-oriented dynamic programming language, hailed by many practitioners as the finest and most useful language available today. When Ruby first burst onto the......一起来看看 《Programming Ruby》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具