接上一篇,sql package的主要实现是database/sql, sql 中比较重要的数据结构是:

  • type DB
  • type Row
  • type Rows
  • type Stmt
  • Type Tx
sql.Register

sql.Register接口用于注册数据库驱动。第三方开发的数据库,需要在init中调用这个接口来完成本驱动的注册。

接口声明为:

func Register(name string, driver driver.Driver)

如果Register被调用了两次注册相同name的driver,或者driver为nil,Register将会panic。

函数Open
func Open(dirverName, dataSourceName string)(*DB error)

Open函数根据传参中的db drivername和driver-specific描述信息打开一个指定的数据库,driver-specific描述信息通常包含database name和连接信息。不同driver所支持的dataSourceName格式可能不同。

Open一般只是验证参数,而不会为database创建连接。如果要验证dataSourceName所指定的连接是否有效,则需要调用Ping函数进行测试。

Open返回一个指向DB类型的指针。DB维护自己的idle connection pool,它可以安全的被多个gorutines并发使用。

Type DB

DB是database/sql中主要的数据结构,定义了各种对database的基本操作方法。

DB是一个代表了零个或更多潜在连接的连接池的database handle。它可以安全的被多个goroutine并发使用。

sql package自动的创建和释放连接,同时它还为空闲连接维护一个空闲池。如果数据库有per-connection state的概念,每个状态只能在一个事物中被可靠的观察到。一旦DB.Begin被调用,它返回的Tx绑定到唯一的一个连接。如果针对一个事物的CommitRollback被调用,则该事务的连接将会被回退到DB的idle connection pool。pool的尺寸可以通过SetMaxIdleConns设置。

type DB struct {
    driver driver.Driver
    dsn string
    numClosed uint64

    mu sync.Mutex
    freeConn []*driverConn
    numOpen int

    openerCh Chan struct{}
    closed bool
    dep map[finalCloser]depSet
    lastPut     map[*driverConn]string
    maxIdle     int
    maxOpen     int
    maxLifetime time.Duration
    cleanerCh   chan struct{}
}

func (*DB) Begin

func (db *DB)Begin() (*Tx, error)

Begin启动一个事务,事务见得隔离级别有driver决定。

func (*DB) Close

func (db *DB)Close() error

Close用于关闭数据库,释放所有打开的资源。关闭数据库是非常少有的操作,因为数据库处理就是要长期存在,并且能够在多个goroutine中共享。

func (*DB) Driver

func (db *DB)Driver() driver.Driver

Driver函数返回数据库的底层驱动信息。

func (*DB) Exec

func (db *DB)Exec(query string, args ...interface{}) (Result, error)

Exec执行的Query不会返回任何行。

type Result是对一条SQL语句执行的总结。Result包含两个方法: LastInsertId返回数据库执行一条命令所产生的整形值。通常是插入一新行时的“auto increment”字段。RowsAffected返回update、insert或delete操作所影响的行号。并不是所有的数据库或数据库驱动都支持这两个特征。

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

func (*DB) Ping

func (db *DB) Ping() error

ping函数用于验证到数据库的连接是否还是alive的状态。如果链接非alive状态,则建立连接。

func (*DB) Prepare

func (db *DB) Prepare(query string) (*Stmt, error)

Prepare函数准备一个statement用于稍后的queries和executions。多个Queries和executions可以通过Prepare返回的statement并发执行。当不再使用statement是,调用者必须调用statement的Close方法关闭statement。

func (*DB) Query

func (db *DB) Query(query string, args ...interface{})(*Rows, error)

Query函数执行对数据库的Query并返回执行结果Rows的指针,可以有后文提到的Rows提供的方法迭代遍历所有的结果。

func (*DB) QueryRow

func (db *DB) QueryRow(query string, args ...interface{}) *Row

QureyRow用于最多只有一行返回的数据库Query操作。所以QueryRow总是会返回一条non-nil操作。对于错误查询或者查询所返回的实际结果为空时,只有Row结构体(对象)的Scan方法被调用时才能体现。

Example:

id := 123
var username string
err := db.QueryRow("SELECT username FROM users WHERE id=?", id).Scan(&username)
switch {
case err == sql.ErrNoRows:
    log.Printf("No user with that ID.")
case err != nil:
    log.Fatal(err)
default:
    fmt.Printf("Username is %s\n", username)
}

func (*DB) SetConnMaxLifetime

func (db *DB)SetConnMaxLifetime(d time.Duration)

SetConnMaxLifetime用于设置连接可被重新使用的最大时间间隔。如果超时,则连接会在重新使用前被关闭。如果 d <= 0, 则连接将被永久保留。

func (*DB) SetMaxIdleConns

func (db *DB)SetMaxIdleConns(n int)

SetMaxIdleConns设置idle connection pool的最大连接数。如果MaxOpenConns的值 > 0,但是小于这里设置的MaxIdleConns,则MaxIdleConns将自动降到与MaxOpenConns的限制相同。

如果 <= 0, 则没有空闲连接会被保留。

func (*DB) SetMaxOpenConns

func (db *DB)SetMaxOpenConns(n int)

SetMaxOpenConns用于设置Database最大可以打开的连接数。

如果 <= 0, 则没有连接限制。且默认值为0(无限制)

func (*DB)Stats

func (db *DB)Stats() DBStats

Stats函数返回数据库的统计信息。

type DBStats struct {
    OpenConnections int
}
Type Row
type Row struct{
}

Row 数据结构只有一个方法Scan

func (r *Row)Scan(dst ...interface{})

Scan将匹配行中的列拷贝到dest所指向的values中。如果有多行匹配,则只返回第一行并丢弃多余的行。如果没有行匹配,则返回**ErrNoRows。dst中的values个数必须与返回的行的列数相同。

Scan将从database中读取的列转换为如下所列的Go的普通类型和sql package提供的特殊类型。

  • *string
  • *[]byte
  • *int, *int8, *int16, *int32, *int64
  • *uint, *uint8, *uint16, *uint32, *uint64
  • *bool
  • *float32, *float64
  • *interface{}
  • *RawBytes

最简单的case是:如果从数据库读取的源数据是interger,bool或string类型等,用T标识该类型,则目标正好是指向T的指针*T ,则scan简单的通过指针将数据分配到目标变量。

Scan同样可以在sting和numeric类型数据间无损转换。Scan直接将从数据库获取的numeric类型数据字符串化(stringify)为string。相反,如果将string转化为numeric类型则需要检查是否溢出等。同理不同类型的numeric之间转换也需要考虑精度问题。*有一个例外就是:如果将scan得到的float64数值字符串化为字符串可能会丢失部分信息。所以通常情况下,将float类型数值转换为float64类型。

如果目标数据类型为*[]byte, Scan将从数据库读取的源数据拷贝到目标地址的对于位置。Copy的副本有调用者拥有,可以被修改,且可以被无限期持有。使用类型*RawBytes可以避免拷贝操作。

如果参数的类型为*interface{},Scan将直接拷贝驱动返回的数据而不做任何的转换。

如果源类型是time.Time,可以被Scan转换为*time.Time, *interface{}, *string或*[]byte类型。转换为*string或*[]byte时,将会使用到time.Format3339Nano

源类型是bool,可以被Scan转换为*bool, *interface{}, *string, *[]byte或*RawBytes

如果要将源数据Scan到*bool类型,则需要元数据是true,false,1,0或者可以被strconv.ParseBool解析的string

Type Rows

Rows结构体类似于type Row,Row是返回单行的Query结果,Rows说多行的Query结果。

type Rows struct {
}

func (*Rows)Close

func (rs *Rows)Close() error

Close将关闭一个Rows,防止仍然能够对Rows继续进行枚举。如果Next的返回结果是false,Rows将被自动关闭,关闭依然能够满足对结果的错误检查。Close是idempotent的,不会影响对结果的错误检查。

func (*Rows)Columns

func (rs *Rows)Columns()([]string, error)

Column返回列的名字列表。如果Rows已经被Close,则返回error。

func (*Rows)Err

func (rs *Rows)Err() error

如果在枚举过程中有Error,Err将返回error。Err可以在显示的或隐式的Close Rows后被调用。

func (*Rows) Next

func (rs *Rows)Next() bool

Next为Scan函数准备下一条结果,如果还有结果则返回true。没有下一行或在准备过程中发生了错误则返回false,Err能够将这两种错误区分开来。

每次调用Scan获取结果前都需要首先执行Next。

func (*Rows) Scan

Rows的Scan同Row的Scan,参考func (*Row)Scan

Type Stmt

Stmt是一个准备状态。Stmt提供一些方法将准备状态执行。Stmt可以安全的被多个goroutine并发执行。

type Stmt struct {
}

Stmt提供如下方法:

func (s *stmt)Close() err
func (s *stmt)Exec(args ...interface{})(Result, error)
func (s *stmt)Query(args ...interface{}) (*Rows, error)
func (s *stmt)QueryRow(args ...interface{}) (*Row, error)

Stmt有调用func (*DB)Prepare返回,所以如下两个方式等效:

stmt,err := db.Prepare(querystring)
res, err := stmt.Exec(...)

等效于:

res, err := db.Exec(querystring, ...)

举一反三,其他方法也是一样。

Tyte Tx
type Tx struct{
}

Tx是正在进行的数据库事务。一次数据库事务必须以Commit或Rollback结束。调用Commit或Rollback后,如果事务失败则会返回错误ErrTxDone。

所以事务由func (*DB)Begin()返回Transaction *Tx开始,以Tx的Commit或Rollback结束。需要注意的是:有Tx的Prepare方法返回的statement也只是有Commit或Rollback关闭,而不是Stmt的Close方法关闭。

func (*Tx) Prepare

func (tx \*Tx)Prepare(query string) (\*stmt, error)

类似于DB的Prepare,Prepare为当前事务创建一个准备好的statement。当事务的的Commit或Rollback被调用后,这个statement将不在有效。

func (*Tx) Commit

func (tx *Tx)Commit() error

Commit提交当前事务。

func (*Tx) Exec

func (tx *Tx)Exec(query string, args ...interface{}) (Result, error)

Exec执行不返回结果的Query,如Insert,Update等。

func (*Tx) Query

func (tx *Tx)Query(query string, args ...interface{}) (*Rows, error)

同前面DB和Stmt的Query

func (*Tx)QueryRow

func (tx *Tx)QueryRow(query string, args ...interface{}) *Row

同DB和Stmt的Query

func (*Tx)Rollback

func (tx *Tx)Rollback() error

Rollback丢弃当前事务的操作。

func (*Tx)Stmt(stmt *stmt) *Stmt

Stmt根据一个已经存在的Stmt返回一个针对当前事务的特定Stmt。

如:

updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?")
...
tx, err := db.Begin()
...
res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203)

上面示例根据statement updateMoney通过Tx.Stmt得到了针对当前事务tx的相同statement。新得到的特殊statement将在tx的Commit或Rollback执行后失效。

参考链接

https://golang.org/src/database/sql/doc.txt
https://godoc.org/database/sql
https://golang.org/src/database/sql/
https://godoc.org/github.com/lib/pq
https://github.com/lib/pq
https://godoc.org/github.com/lib/pq
http://jmoiron.github.io/sqlx/
https://github.com/jmoiron/sqlx/blob/master/sqlx.go
https://github.com/golang/go/wiki/SQLInterface
https://github.com/golang/go/wiki/SQLDrivers