使用mysql数据库

  1. package main
  2. import (
  3. "database/sql"
  4. "fmt"
  5. _ "github.com/go-sql-driver/mysql"
  6. )
  7. func checkErr(err error) {
  8. if err != nil {
  9. panic(err)
  10. }
  11. }
  12. func main() {
  13. //sql.Open()函数用来打开一个注册过的数据库驱动
  14. db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/d?charset=utf8")
  15. checkErr(err)
  16. defer db.Close()
  17. //插入数据
  18. stmt, err := db.Prepare("insert userinfo set username=?,departname=?,created=?")
  19. checkErr(err)
  20. res, err := stmt.Exec("jack", "研发部门", "2012-12-09")
  21. checkErr(err)
  22. id, err := res.LastInsertId()
  23. checkErr(err)
  24. fmt.Println(id)
  25. //更新数据
  26. stmt, err = db.Prepare("update userinfo set username=? where uid=?")
  27. checkErr(err)
  28. res, err = stmt.Exec("tom", id)
  29. checkErr(err)
  30. affected, err := res.RowsAffected()
  31. checkErr(err)
  32. fmt.Println(affected)
  33. //查询数据
  34. rows, err := db.Query("select * from userinfo")
  35. checkErr(err)
  36. for rows.Next() {
  37. var uid int
  38. var username string
  39. var department string
  40. var created string
  41. err := rows.Scan(&uid, &username, &department, &created)
  42. checkErr(err)
  43. fmt.Println(uid, username, department, created)
  44. }
  45. //删除数据
  46. stmt, err = db.Prepare("delete from userinfo where uid=?")
  47. checkErr(err)
  48. res, err = stmt.Exec(id)
  49. checkErr(err)
  50. rowsAffected, err := res.RowsAffected()
  51. checkErr(err)
  52. fmt.Println(rowsAffected)
  53. }

sql.Open()函数用来打开一个注册过的数据库驱动
go-sql-driver中注册了mysql这个数据库驱动,第二个参数是DSN(Data Source Name),它是go-sql-driver定义的一些数据库链接和配置信息。它支持如下格式:

  1. user@unix(/path/to/socket)/dbname?charset=utf8
  2. user:password@tcp(localhost:5555)/dbname?charset=utf8
  3. user:password@/dbname
  4. user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname

db.Prepare()函数用来返回准备要执行的sql操作,然后返回准备完毕的执行状态。
db.Query()函数用来直接执行Sql返回Rows结果。
stmt.Exec()函数用来执行stmt准备好的SQL语句

sql接口

driver.Driver

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

  1. type Driver interface {
  2. Open(name string) (Conn, error)
  3. }

第三方驱动都会定义这个函数,它会解析name参数来获取相关数据库的连接信息,解析完成后,它将使用此信息来初始化一个Conn并返回它。

driver.Conn

Conn是一个数据库连接的接口定义,他定义了一系列方法,这个Conn只能应用在一个goroutine里面,不能使用在多个goroutine里面

  1. type Conn interface {
  2. Prepare(query string) (Stmt, error)
  3. Close() error
  4. Begin() (Tx, error)
  5. }

Prepare函数返回与当前连接相关的执行Sql语句的准备状态,可以进行查询、删除等操作。
Close函数关闭当前的连接,执行释放连接拥有的资源等清理工作。因为驱动实现了database/sql里面建议的conn pool,所以你不用再去实现缓存conn之类的,这样会容易引起问题。
Begin函数返回一个代表事务处理的Tx,通过它你可以进行查询,更新等操作,或者对事务进行回滚、递交。

driver.Stmt

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

  1. type Stmt interface {
  2. Close() error
  3. NumInput() int
  4. Exec(args []Value) (Result, error)
  5. Query(args []Value) (Rows, error)
  6. }

Close函数关闭当前的链接状态,但是如果当前正在执行query,query还是有效返回rows数据。
NumInput函数返回当前预留参数的个数,当返回>=0时数据库驱动就会智能检查调用者的参数。当数据库驱动包不知道预留参数的时候,返回-1。
Exec函数执行Prepare准备好的sql,传入参数执行update/insert等操作,返回Result数据
Query函数执行Prepare准备好的sql,传入需要的参数执行select操作,返回Rows结果集

driver.Tx

务处理一般就两个过程,递交或者回滚。数据库驱动里面也只需要实现这两个函数就可以

  1. type Tx interface {
  2. Commit() error
  3. Rollback() error
  4. }
  5. //这两个函数一个用来递交一个事务,一个用来回滚事务。

driver.Execer

这是一个Conn可选择实现的接口

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

如果这个接口没有定义,那么在调用DB.Exec,就会首先调用Prepare返回Stmt,然后执行Stmt的Exec,然后关闭Stmt。

driver.Result

这个是执行Update/Insert等操作返回的结果接口定义

  1. type Result interface {
  2. LastInsertId() (int64, error)
  3. RowsAffected() (int64, error)
  4. }

LastInsertId函数返回由数据库执行插入操作得到的自增ID号。
RowsAffected函数返回query操作影响的数据条目数。

driver.Rows

Rows是执行查询返回的结果集接口定义

  1. type Rows interface {
  2. Columns() []string
  3. Close() error
  4. Next(dest []Value) error
  5. }

Columns函数返回查询数据库表的字段信息,这个返回的slice和sql查询的字段一一对应,而不是返回整个表的所有字段。
Close函数用来关闭Rows迭代器。
Next函数用来返回下一条数据,把数据赋值给dest。dest里面的元素必须是driver.Value的值除了string,返回的数据里面所有的string都必须要转换成[]byte。如果最后没数据了,Next函数最后返回io.EOF。

driver.RowsAffected

RowsAffected其实就是一个int64的别名,但是他实现了Result接口,用来底层实现Result的表示方式

  1. type RowsAffected int64
  2. func (RowsAffected) LastInsertId() (int64, error)
  3. func (v RowsAffected) RowsAffected() (int64, error)

driver.Value

Value其实就是一个空接口,他可以容纳任何的数据
type Value interface{}
drive的Value是驱动必须能够操作的Value,Value要么是nil,要么是下面的任意一种

  1. int64
  2. float64
  3. bool
  4. []byte
  5. string [*]除了Rows.Next返回的不能是string.
  6. time.Time

driver.ValueConverter

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

  1. type ValueConverter interface {
  2. ConvertValue(v interface{}) (Value, error)
  3. }

在开发的数据库驱动包里面实现这个接口的函数在很多地方会使用到,这个ValueConverter有很多好处:

  • 转化driver.value到数据库表相应的字段,例如int64的数据如何转化成数据库表uint16字段
  • 把数据库查询结果转化成driver.Value值
  • 在scan函数里面如何把driver.Value值转化成用户定义的值

driver.Valuer

Valuer接口定义了返回一个driver.Value的方式

  1. type Valuer interface {
  2. Value() (Value, error)
  3. }

database/sql

database/sql在database/sql/driver提供的接口基础上定义了一些更高阶的方法,用以简化数据库操作,同时内部还建议性地实现一个conn pool。

  1. type DB struct {
  2. driver driver.Driver
  3. dsn string
  4. mu sync.Mutex // protects freeConn and closed
  5. freeConn []driver.Conn
  6. closed bool
  7. }

我们可以看到Open函数返回的是DB对象,里面有一个freeConn,它就是那个简易的连接池。它的实现相当简单或者说简陋,就是当执行db.prepare -> db.prepareDC的时候会defer dc.releaseConn,然后调用db.putConn,也就是把这个连接放入连接池,每次调用db.conn的时候会先判断freeConn的长度是否大于0,大于0说明有可以复用的conn,直接拿出来用就是了,如果不大于0,则创建一个conn,然后再返回之。