本文通过介绍面向mysql数据库的基本操作,包括;建库建表以及最基本的增删查改,来简单探究database/sql以及github.com/go-sql-driver/mysql的实现原理。
1. 创建数据库
使用Open()初始化相关的资源和配置,在使用Open()时不需要指定数据库的名字(因为此时还没有数据库),代码如下:
代码块一
1 import (2 "database/sql"
3 "fmt"
4
5 _ "github.com/go-sql-driver/mysql"
6 )7 ...8 varerr error9 sqlStat := fmt.Sprintf("%s:%s@(%s)/", "root", "123", "127.0.0.1:3306")10 dbtmp, err := sql.Open("mysql", sqlStat)11 if err !=nil {12 panic(err)13 }
那Open()在底层做了什么,让我们来一探究竟。
代码块二
1 func Open(driverName, dataSourceName string) (*DB, error) {2 driversMu.RLock()3 driveri, ok :=drivers[driverName]4 driversMu.RUnlock()5 if !ok {6 return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)7 }8
9 if driverCtx, ok :=driveri.(driver.DriverContext); ok {10 connector, err :=driverCtx.OpenConnector(dataSourceName)11 if err !=nil {12 returnnil, err13 }14 returnOpenDB(connector), nil15 }16
17 returnOpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil18 }
如上,首先从数据库驱动缓存里面取出mysql对应的驱动器,读者可能会问到:此驱动缓存是何时将各种数据库的驱动添加进去的呢?
其实它不是由于sql包主动添加的,而是sql包对外提供了一个Register()接口,用于注册数据库驱动,代码如下:
代码块三
1 func Register(name string, driver driver.Driver) {2 driversMu.Lock()3 defer driversMu.Unlock()4 if driver ==nil {5 panic("sql: Register driver is nil")6 }7 if _, dup :=drivers[name]; dup {8 panic("sql: Register called twice for driver" +name)9 }10 drivers[name] =driver11 }
那mysql是何时注册自己的数据库驱动的呢?我们看到代码块一的第5行,在包名之前添加下划线的作用是只执行该包的init方法,而不需要导入整个包。关于Go语言程序模块的初始化顺序,参见https://studygolang.com/articles/6464。
那我们再来看看mysql数据库驱动包中的init方法做了什么:
1 func init() {2 sql.Register("mysql", &MySQLDriver{})3 }
果然如此,mysql数据库驱动包在初始化的时候向sql包注册了自己的驱动。
我们回到代码块二,在第9行,直接将driveri断言成了DriverContext接口类型,这里driveri虽然是Driver类型,但是由上述分析可知其动态值是MySQLDriver类型,根据Go语言特性,一个接口类型可直接断言成另外一个接口类型,那么其动态类型必须实现后者,所以我们看看MySQLDriver是否实现了DriverContext接口:
代码块四
1 //If a Driv