update some words in 4.*md and 5.1md
This commit is contained in:
39
5.1.md
39
5.1.md
@@ -1,5 +1,5 @@
|
||||
# 5.1 database/sql接口
|
||||
Go和PHP不同的地方是,他没有官方提供数据库驱动,而是为开发数据库驱动定义了一些标准接口,第三方用户可以根据定义的接口来开发相应的数据库驱动,这样做有一个好处,我们按照标准接口开发的代码, 在需要迁移数据库时,不需要任何修改。那么Go都定义了那些标准接口呢?让我们来详细的分析一下
|
||||
Go与PHP不同的地方是Go没有官方提供数据库驱动,而是为开发者开发数据库驱动定义了一些标准接口,开发者可以根据定义的接口来开发相应的数据库驱动,这样做有一个好处,只要按照标准接口开发的代码, 以后需要迁移数据库时,不需要任何修改。那么Go都定义了哪些标准接口呢?让我们来详细的分析一下
|
||||
|
||||
## sql.Register
|
||||
这个存在于database/sql的函数是用来注册数据库驱动的,当第三方开发者开发数据库驱动时,都会实现init函数,在init里面会调用这个`Register(name string, driver driver.Driver)`完成本驱动的注册。
|
||||
@@ -19,7 +19,7 @@ Go和PHP不同的地方是,他没有官方提供数据库驱动,而是为开
|
||||
sql.Register("mymysql", &d)
|
||||
}
|
||||
|
||||
我们看到第三方驱动都是通过调用这个函数来注册自己的驱动名称以及相应的driver。在database/sql内部通过一个map来存储相应的驱动。
|
||||
我们看到第三方数据库驱动都是通过调用这个函数来注册自己的数据库驱动名称以及相应的driver实现。在database/sql内部通过一个map来存储用户定义的相应驱动。
|
||||
|
||||
var drivers = make(map[string]driver.Driver)
|
||||
|
||||
@@ -27,12 +27,16 @@ Go和PHP不同的地方是,他没有官方提供数据库驱动,而是为开
|
||||
|
||||
因此通过database/sql的注册函数可以同时注册多个数据库驱动,只要不重复。
|
||||
|
||||
>在我们使用database/sql接口和第三方库的时候经常看到如下的import
|
||||
>在我们使用database/sql接口和第三方库的时候经常看到如下:
|
||||
|
||||
"database/sql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
> import (
|
||||
> "database/sql"
|
||||
> _ "github.com/mattn/go-sqlite3"
|
||||
> )
|
||||
|
||||
>新手都会被这个`_`所迷惑,其实这个就是Go设计的巧妙之处,我们在变量赋值的时候经常看到这个,它是用来忽略变量的占位符,那么这个包引入也是,这儿的意思是引入此包而不直接使用这个包中定义的函数,变量等资源,我们在2.3流程和函数里面介绍过init函数的初始化过程,包在引入的时候会去调用包的init函数以完成对包的初始化,因此我们引入上面的数据库驱动包之后会去调用init函数,然后在init函数里面注册了这个数据库驱动,这样我们就可以在接下来的代码中直接使用这个数据库驱动了。
|
||||
>新手都会被这个`_`所迷惑,其实这个就是Go设计的巧妙之处,我们在变量赋值的时候经常看到这个符号,它是用来忽略变量赋值的占位符,那么包引入用到这个符号也是相似的作用,这儿使用`_`的意思是引入后面的包名而不直接使用这个包中定义的函数,变量等资源。
|
||||
|
||||
>我们在2.3节流程和函数的一节中介绍过init函数的初始化过程,包在引入的时候会自动调用包的init函数以完成对包的初始化。因此,我们引入上面的数据库驱动包之后要手动去调用init函数,然后在init函数里面注册这个数据库驱动,这样我们就可以在接下来的代码中直接使用这个数据库驱动了。
|
||||
|
||||
## driver.Driver
|
||||
Driver是一个数据库驱动的接口,他定义了一个method: Open(name string),这个方法返回一个数据库的Conn接口。
|
||||
@@ -48,7 +52,7 @@ Driver是一个数据库驱动的接口,他定义了一个method: Open(name
|
||||
go goroutineB (Conn) //执行插入操作
|
||||
...
|
||||
|
||||
上面这样的代码可能会导致不知道某个操作究竟是由哪个goroutine发起的,从而数据混乱,比如可能会把goroutine A里面执行的查询操作的结果返回给goroutine B从而使B错误的把此结果当成自己执行的插入结果。
|
||||
上面这样的代码可能会使Go不知道某个操作究竟是由哪个goroutine发起的,从而导致数据混乱,比如可能会把goroutineA里面执行的查询操作的结果返回给goroutineB从而使B错误地把此结果当成自己执行的插入数据。
|
||||
|
||||
第三方驱动都会定义这个函数,它会解析name参数来获取相关数据库的连接信息,解析完成后,它将使用此信息来初始化一个Conn并返回它。
|
||||
|
||||
@@ -61,14 +65,14 @@ Conn是一个数据库连接的接口定义,他定义了一系列方法,这
|
||||
Begin() (Tx, error)
|
||||
}
|
||||
|
||||
Prepare函数返回与当前连接相关的准备好Sql语句的状态,可以进行查询、删除等操作。
|
||||
Prepare函数返回与当前连接相关的执行Sql语句的准备状态,可以进行查询、删除等操作。
|
||||
|
||||
Close函数关闭当前的连接,以及执行释放连接拥有的资源等清理工作。因为database/sql里面实现了建议的conn pool,所以你不要再自己去实现缓存conn之类的,这样容易引起问题。
|
||||
Close函数关闭当前的连接,执行释放连接拥有的资源等清理工作。因为驱动实现了database/sql里面建议的conn pool,所以你不用再去实现缓存conn之类的,这样会容易引起问题。
|
||||
|
||||
Begin函数返回一个代表事务处理的Tx,通过它你可以进行查询,更新等操作,或者对事务进行回滚、递交。
|
||||
|
||||
## driver.Stmt
|
||||
Stmt是一种准备好的状态,和Conn相关联,而且是只能应用于一个goroutine中,不能应用在多个goroutine中。
|
||||
Stmt是一种准备好的状态,和Conn相关联,而且只能应用于一个goroutine中,不能应用于多个goroutine。
|
||||
|
||||
type Stmt interface {
|
||||
Close() error
|
||||
@@ -113,7 +117,7 @@ Query函数执行Prepare准备好的sql,传入需要的参数执行select操
|
||||
RowsAffected() (int64, error)
|
||||
}
|
||||
|
||||
LastInsertId函数返回由数据库执行插入操作得到的自动增长ID号。
|
||||
LastInsertId函数返回由数据库执行插入操作得到的自增ID号。
|
||||
|
||||
RowsAffected函数返回query操作影响的数据条目数。
|
||||
|
||||
@@ -126,7 +130,7 @@ Rows是执行查询返回的结果集接口定义
|
||||
Next(dest []Value) error
|
||||
}
|
||||
|
||||
Columns函数返回查询数据库表的字段信息,这个返回的slice和你sql查询的字段一一对应,而不是返回整个表的所有字段。
|
||||
Columns函数返回查询数据库表的字段信息,这个返回的slice和sql查询的字段一一对应,而不是返回整个表的所有字段。
|
||||
|
||||
Close函数用来关闭Rows迭代器。
|
||||
|
||||
@@ -147,7 +151,7 @@ Value其实就是一个空接口,他可以容纳任何的数据
|
||||
|
||||
type Value interface{}
|
||||
|
||||
Value的值必须所有的驱动里面控制的,Value要么是nil,要么是下面的任意一种
|
||||
drive的Value是驱动必须能够操作的Value,Value要么是nil,要么是下面的任意一种
|
||||
|
||||
int64
|
||||
float64
|
||||
@@ -175,22 +179,23 @@ Valuer接口定义了返回一个driver.Value的方式
|
||||
type Valuer interface {
|
||||
Value() (Value, error)
|
||||
}
|
||||
|
||||
很多类型都实现了这个Value方法,用来自身与driver.Value的转化。
|
||||
|
||||
通过上面的讲解,你应该对于驱动的开发有了一个基本的了解,一个驱动只要实现了这些接口就能完成增删查改等基本操作了,剩下的就是与相应的数据库进行数据交互等细节问题了,在此不再赘述。
|
||||
|
||||
## database/sql
|
||||
database/sql在database/sql/driver提供的接口基础上定义了一些更高阶的方法,用以简化数据库操作,同时内部还实现了一个建议的conn pool。
|
||||
database/sql在database/sql/driver提供的接口基础上定义了一些更高阶的方法,用以简化数据库操作,同时内部还建议性地实现一个conn pool。
|
||||
|
||||
type DB struct {
|
||||
driver driver.Driver
|
||||
dsn string
|
||||
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,然后再返回之。
|
||||
我们可以看到Open函数返回的是DB对象,里面有一个freeConn,它就是那个简易的连接池。它的实现相当简单或者说简陋,就是当执行Db.prepare的时候会`defer db.putConn(ci, err)`,也就是把这个连接放入连接池,每次调用conn的时候会先判断freeConn的长度是否大于0,大于0说明有可以复用的conn,直接拿出来用就是了,如果不大于0,则创建一个conn,然后再返回之。
|
||||
|
||||
|
||||
## links
|
||||
|
||||
Reference in New Issue
Block a user