diff --git a/zh/05.1.md b/zh/05.1.md index f98163f8..81e2fbbb 100644 --- a/zh/05.1.md +++ b/zh/05.1.md @@ -7,25 +7,25 @@ Go与PHP不同的地方是Go官方没有提供数据库驱动,而是为开发 我们来看一下mymysql、sqlite3的驱动里面都是怎么调用的: ```Go - //https://github.com/mattn/go-sqlite3驱动 - func init() { - sql.Register("sqlite3", &SQLiteDriver{}) - } +//https://github.com/mattn/go-sqlite3驱动 +func init() { + sql.Register("sqlite3", &SQLiteDriver{}) +} - //https://github.com/mikespook/mymysql驱动 - // Driver automatically registered in database/sql - var d = Driver{proto: "tcp", raddr: "127.0.0.1:3306"} - func init() { - Register("SET NAMES utf8") - sql.Register("mymysql", &d) - } +//https://github.com/mikespook/mymysql驱动 +// Driver automatically registered in database/sql +var d = Driver{proto: "tcp", raddr: "127.0.0.1:3306"} +func init() { + Register("SET NAMES utf8") + sql.Register("mymysql", &d) +} ``` 我们看到第三方数据库驱动都是通过调用这个函数来注册自己的数据库驱动名称以及相应的driver实现。在database/sql内部通过一个map来存储用户定义的相应驱动。 ```Go - var drivers = make(map[string]driver.Driver) +var drivers = make(map[string]driver.Driver) - drivers[name] = driver +drivers[name] = driver ``` 因此通过database/sql的注册函数可以同时注册多个数据库驱动,只要不重复。 @@ -44,17 +44,17 @@ Go与PHP不同的地方是Go官方没有提供数据库驱动,而是为开发 Driver是一个数据库驱动的接口,他定义了一个method: Open(name string),这个方法返回一个数据库的Conn接口。 ```Go - type Driver interface { - Open(name string) (Conn, error) - } +type Driver interface { + Open(name string) (Conn, error) +} ``` 返回的Conn只能用来进行一次goroutine的操作,也就是说不能把这个Conn应用于Go的多个goroutine里面。如下代码会出现错误 ```Go - ... - go goroutineA (Conn) //执行查询操作 - go goroutineB (Conn) //执行插入操作 - ... +... +go goroutineA (Conn) //执行查询操作 +go goroutineB (Conn) //执行插入操作 +... ``` 上面这样的代码可能会使Go不知道某个操作究竟是由哪个goroutine发起的,从而导致数据混乱,比如可能会把goroutineA里面执行的查询操作的结果返回给goroutineB从而使B错误地把此结果当成自己执行的插入数据。 @@ -64,11 +64,11 @@ Driver是一个数据库驱动的接口,他定义了一个method: Open(name Conn是一个数据库连接的接口定义,他定义了一系列方法,这个Conn只能应用在一个goroutine里面,不能使用在多个goroutine里面,详情请参考上面的说明。 ```Go - type Conn interface { - Prepare(query string) (Stmt, error) - Close() error - Begin() (Tx, error) - } +type Conn interface { + Prepare(query string) (Stmt, error) + Close() error + Begin() (Tx, error) +} ``` Prepare函数返回与当前连接相关的执行Sql语句的准备状态,可以进行查询、删除等操作。 @@ -80,12 +80,12 @@ Begin函数返回一个代表事务处理的Tx,通过它你可以进行查询, Stmt是一种准备好的状态,和Conn相关联,而且只能应用于一个goroutine中,不能应用于多个goroutine。 ```Go - type Stmt interface { - Close() error - NumInput() int - Exec(args []Value) (Result, error) - Query(args []Value) (Rows, error) - } +type Stmt interface { + Close() error + NumInput() int + Exec(args []Value) (Result, error) + Query(args []Value) (Rows, error) +} ``` Close函数关闭当前的链接状态,但是如果当前正在执行query,query还是有效返回rows数据。 @@ -100,10 +100,10 @@ Query函数执行Prepare准备好的sql,传入需要的参数执行select操 事务处理一般就两个过程,递交或者回滚。数据库驱动里面也只需要实现这两个函数就可以 ```Go - type Tx interface { - Commit() error - Rollback() error - } +type Tx interface { + Commit() error + Rollback() error +} ``` 这两个函数一个用来递交一个事务,一个用来回滚事务。 @@ -111,9 +111,9 @@ Query函数执行Prepare准备好的sql,传入需要的参数执行select操 这是一个Conn可选择实现的接口 ```Go - type Execer interface { - Exec(query string, args []Value) (Result, error) - } +type Execer interface { + Exec(query string, args []Value) (Result, error) +} ``` 如果这个接口没有定义,那么在调用DB.Exec,就会首先调用Prepare返回Stmt,然后执行Stmt的Exec,然后关闭Stmt。 @@ -121,10 +121,10 @@ Query函数执行Prepare准备好的sql,传入需要的参数执行select操 这个是执行Update/Insert等操作返回的结果接口定义 ```Go - type Result interface { - LastInsertId() (int64, error) - RowsAffected() (int64, error) - } +type Result interface { + LastInsertId() (int64, error) + RowsAffected() (int64, error) +} ``` LastInsertId函数返回由数据库执行插入操作得到的自增ID号。 @@ -134,11 +134,11 @@ RowsAffected函数返回query操作影响的数据条目数。 Rows是执行查询返回的结果集接口定义 ```Go - type Rows interface { - Columns() []string - Close() error - Next(dest []Value) error - } +type Rows interface { + Columns() []string + Close() error + Next(dest []Value) error +} ``` Columns函数返回查询数据库表的字段信息,这个返回的slice和sql查询的字段一一对应,而不是返回整个表的所有字段。 @@ -151,35 +151,35 @@ Next函数用来返回下一条数据,把数据赋值给dest。dest里面的 RowsAffected其实就是一个int64的别名,但是他实现了Result接口,用来底层实现Result的表示方式 ```Go - type RowsAffected int64 +type RowsAffected int64 - func (RowsAffected) LastInsertId() (int64, error) +func (RowsAffected) LastInsertId() (int64, error) - func (v RowsAffected) RowsAffected() (int64, error) +func (v RowsAffected) RowsAffected() (int64, error) ``` ## driver.Value Value其实就是一个空接口,他可以容纳任何的数据 ```Go - type Value interface{} +type Value interface{} ``` drive的Value是驱动必须能够操作的Value,Value要么是nil,要么是下面的任意一种 ```Go - int64 - float64 - bool - []byte - string [*]除了Rows.Next返回的不能是string. - time.Time +int64 +float64 +bool +[]byte +string [*]除了Rows.Next返回的不能是string. +time.Time ``` ## driver.ValueConverter ValueConverter接口定义了如何把一个普通的值转化成driver.Value的接口 ```Go - type ValueConverter interface { - ConvertValue(v interface{}) (Value, error) - } +type ValueConverter interface { + ConvertValue(v interface{}) (Value, error) +} ``` 在开发的数据库驱动包里面实现这个接口的函数在很多地方会使用到,这个ValueConverter有很多好处: @@ -191,9 +191,9 @@ ValueConverter接口定义了如何把一个普通的值转化成driver.Value的 Valuer接口定义了返回一个driver.Value的方式 ```Go - type Valuer interface { - Value() (Value, error) - } +type Valuer interface { + Value() (Value, error) +} ``` 很多类型都实现了这个Value方法,用来自身与driver.Value的转化。 @@ -203,13 +203,13 @@ Valuer接口定义了返回一个driver.Value的方式 database/sql在database/sql/driver提供的接口基础上定义了一些更高阶的方法,用以简化数据库操作,同时内部还建议性地实现一个conn pool。 ```Go - type DB struct { - driver driver.Driver - dsn string - mu sync.Mutex // protects freeConn and closed - freeConn []driver.Conn - closed bool - } +type DB struct { + driver driver.Driver + dsn string + mu sync.Mutex // protects freeConn and closed + freeConn []driver.Conn + closed bool +} ``` 我们可以看到Open函数返回的是DB对象,里面有一个freeConn,它就是那个简易的连接池。它的实现相当简单或者说简陋,就是当执行Db.prepare的时候会`defer db.putConn(ci, err)`,也就是把这个连接放入连接池,每次调用conn的时候会先判断freeConn的长度是否大于0,大于0说明有可以复用的conn,直接拿出来用就是了,如果不大于0,则创建一个conn,然后再返回之。