本次分享下 - 数据库 - Go中的数据库驱动程序接口设计
数据库
对于Web开发人员而言,数据库是Web开发的核心。
您几乎可以将任何内容保存到数据库中,并查询或更新其中的数据,如用户信息,产品或新闻文章。
Go不提供任何数据库驱动程序,但它确实在database/sql包中定义了驱动程序接口。
我们可以基于该接口开发数据库驱动程序。首先先看下 “Go中的数据库驱动程序接口设计”
Go中的数据库驱动程序接口设计
Go不提供任何官方数据库驱动程序,不像PHP这样的其他语言。
但是,它确实有一些数据库驱动程序接口标准,供开发人员使用。
优点是,如果您的代码是根据这些接口标准开发的,那么如果您的数据库发生更改,则无需更改任何代码。
让我们看看这些数据库接口标准是什么。
sql.Register
当您使用第三方数据库驱动程序时,此函数位于database/sql包中,用于注册数据库驱动程序。
所有这些都应该在init()中调用Register(name string,driver driver.Driver)函数以便自己注册。
我们来看看相应的mymysql和sqlite3驱动代码:
// https://github.com/mattn/go-sqlite3 driver
func init() {
sql.Register("sqlite3", &SQLiteDriver{})
}
// https://github.com/mikespook/mymysql driver
// 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)
}
我们看到所有第三方数据库驱动程序都实现了这个函数来自我注册,Go使用map来保存database/sql中的用户驱动程序。
var drivers = make(map[string]driver.Driver)
drivers[name] = driver
因此,此注册功能可以根据需要注册多个驱动程序,每个驱动程序具有不同的名称。
当我们使用第三方驱动程序时,我们总是会看到以下代码:
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
对于许多初学者来说,下划线(也称为'空白')```_```可能会让人感到困惑,但这是Go中的一个很棒的功能。
我们已经知道这个下划线标识符用于从函数返回中丢弃值,并且还必须使用在Go中的代码中导入的所有包。
因此,当空白与导入一起使用时,这意味着您需要执行该包的init()函数而不直接使用它,这非常适合注册数据库驱动程序的用例。
driver.Driver
Driver是一个包含Open(名称字符串)方法的接口,该方法返回Conn接口。
type Driver interface {
Open(name string) (Conn, error)
}
这是一次性Conn,这意味着每个goroutine只能使用一次。
以下代码将导致错误发生:
go goroutineA (Conn) // query
go goroutineB (Conn) // insert
因为Go不知道哪个goroutine执行哪个操作,查询操作可能会得到插入操作的结果,反之亦然。
所有第三方驱动程序都应该使用此函数来解析Conn的名称并返回正确的结果。
driver.Conn
这是一个带有一些方法的数据库连接接口,正如我上面所说的,每个goroutine只能使用一次相同的Conn。
type Conn interface {
Prepare(query string) (Stmt, error)
Close() error
Begin() (Tx, error)
}
- Prepare 返回相应SQL命令的准备状态,用于查询和删除等。
- Close 关闭当前连接并清理资源。大多数第三方驱动程序实现某种连接池,因此您不需要缓存可能导致意外错误的连接。
- Begin 返回表示事务句柄的Tx。您可以使用它来查询,更新,回滚事务等。
driver.Stmt
这是一个与Conn对应的就绪状态,因此每个goroutine只能使用一次(与Conn一样)。
type Stmt interface {
Close() error
NumInput() int
Exec(args []Value) (Result, error)
Query(args []Value) (Rows, error)
}
- Close 关闭当前连接但如果正在执行查询操作,仍会返回行数据。NumInput返回专性参数的数量。数据库驱动程序应在结果大于0时检查其调用者的参数,并在数据库驱动程序不知道任何可能的参数时返回-1。
- Exec 执行在Prepare中准备的update/insert SQL命令,返回Result。
- Query 执行Prepare中准备的select SQL命令,返回行数据。
driver.Tx
通常,事务句柄只有提交或回滚方法,而数据库驱动程序只需要实现这两种方法。
type Tx interface {
Commit() error
Rollback() error
}
driver.Execer
这是一个可选的接口。
type Execer interface {
Exec(query string, args []Value) (Result, error)
}
如果驱动程序没有实现此接口,则在调用DB.Exec时,它将自动调用Prepare,然后返回Stmt。之后,它执行Stmt的Exec方法,然后关闭Stmt。
driver.Result
这是update/insert操作结果的接口。
type Result interface {
LastInsertId() (int64, error)
RowsAffected() (int64, error)
}
- LastInsertId 在数据库插入操作后返回自动增量Id编号。
- RowsAffected 返回受查询操作影响的行。
driver.Rows
这是查询操作结果的接口。
type Rows interface {
Columns() []string
Close() error
Next(dest []Value) error
}
- Columns 返回数据库表的字段信息。切片仅与SQL查询字段一一对应,并且不返回该数据库表的所有字段。
- Close 关闭行迭代器。
- Next 返回下一个数据并分配给dest,将所有字符串转换为字节数组,如果没有更多可用数据,则会收到io.EOF错误。
driver.RowsAffected
这是int64的别名,但它实现了Result接口。
type RowsAffected int64
func (RowsAffected) LastInsertId() (int64, error)
func (v RowsAffected) RowsAffected() (int64, error)
driver.Value
这是一个空接口,可以包含任何类型的数据。
type Value interface{}
值必须是驱动程序可以运行的值或者为零,因此它应该是以下类型之一:
int64
float64
bool
[]byte
string [*] Except Rows.Next which cannot return string
time.Time
driver.ValueConverter
这定义了一个用于将正常值转换为driver.Value的接口。
type ValueConverter interface {
ConvertValue(v interface{}) (Value, error)
}
此接口通常用于数据库驱动程序,并具有许多有用的功能:
- 将driver.Value转换为相应的数据库字段类型,例如将int64转换为uint16。
- 将数据库查询结果转换为driver.Value。
- 将driver.Value转换为scan函数中的用户定义值。
driver.Valuer
这定义了一个返回driver.Value的接口。
type Valuer interface {
Value() (Value, error)
}
许多类型实现此接口以在driver.Value和其自身之间进行转换。
此时,您应该了解一下在Go中开发数据库驱动程序。
一旦您可以为添加,删除,更新等操作实现接口,与特定数据库通信只会留下一些问题。
database/sql
在database/sql/driver之上定义更多高级方法,以便更方便地进行数据库操作,并建议您实现连接池。
type DB struct {
driver driver.Driver
dsn string
mu sync.Mutex // protects freeConn and closed
freeConn []driver.Conn
closed bool
}
如您所见,Open函数返回一个具有freeConn的DB,这是一个简单的连接池。
它的实现非常简单和丑陋。
它在Db.prepare函数中使用延迟db.putConn(ci,err)将连接放入连接池。
每次调用Conn函数时,它都会检查freeConn的长度。
如果它大于0,则表示存在可重用的连接,并且它直接返回给您。
否则,它会创建一个新连接并返回。
今天就分享到这里,如果你有其他疑问请在下方留言或者加群交流
版权声明
由 durban创作并维护的 小绒毛的足迹博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证。
本文首发于
博客(
https://www.xiaorongmao.com ),版权所有,侵权必究。