[Golang数据库专题1]datbase/sql接口

目录

一 、引言

1.1 概述

1.2 包接口

二、database/sql接口(常用)

1.sql.Register

2.driver.Driver

3. driver.Conn

4. driver.Stmt

5.driver.Tx

6.driver.Execer

7.driver.Result

8.driver.Rows

9.driver.RowsAffected

10.driver.Value

11.driver.ValueConverter

12.driver.Valuer

三、结语


一 、引言

1.1 概述

Go语言没有内置的驱动支持任何数据库,但Go语言定义了database/sql接口,用户可以基于驱动接口开发相应数据库的驱动。

可以理解database/sql是Go语言操作数据库的一种公共协议,操作各种数据库有不同的内部实现,但对外暴露的接口是一致的,便于不同数据库之间的迁移。

1.2 包接口

该包接口如下图:

二、database/sql接口(常用)

Go语言为开发数据库驱动定义了一些标准接口,使用标准接口开发的代码,在迁移数据库时,不需要做任何修改(当然双方数据库都遵守标准接口)。

以下将逐一分析Go语言定义的标准接口:

1.sql.Register

该函数用来注册数据库驱动。当第三方开发者开发数据库驱动时,都会实现init函数,而init中会调用 Register(name string, driver driver.Driver) 完成驱动注册。如 github.com/go-sql-driver/mysql

中驱动调用该方法:

// init中注册驱动
func init() {
	sql.Register("mysql", &MySQLDriver{})
}
github.com/go-sql-driver/mysql 驱动中该方法源码:
// Register makes a database driver available by the provided name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
func Register(name string, driver driver.Driver) {
	driversMu.Lock()
	defer driversMu.Unlock()
	if driver == nil {
		panic("sql: Register driver is nil")
	}
	if _, dup := drivers[name]; dup {
		panic("sql: Register called twice for driver " + name)
	}
	drivers[name] = driver
}

ps:

  1. database/sql的注册函数可以同时注册多个数据库驱动,主要不重复即可。
  2. 包在引入的时候会自动调用包init函数完成对包的初始化,即引入数据库驱动包后会自动调用init函数完成驱动注册,以供后续代码直接使用该数据库驱动。

2.driver.Driver

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

// Driver is the interface that must be implemented by a database
// driver.
//
// Database drivers may implement DriverContext for access
// to contexts and to parse the name only once for a pool of connections,
// instead of once per connection.
type Driver interface {
	// Open returns a new connection to the database.
	// The name is a string in a driver-specific format.
	//
	// Open may return a cached connection (one previously
	// closed), but doing so is unnecessary; the sql package
	// maintains a pool of idle connections for efficient re-use.
	//
	// The returned connection is only used by one goroutine at a
	// time.
	Open(name string) (Conn, error)
}

以上接口返回的Conn只能进行一次goroutine的操作,不能将该conn应用于Go语言的多个goroutine里面。如下代码会出现错误:

//  这种写法会让Go语言不知道某个操作究竟是由哪个goroutine发起的,从而导致数据混乱
//  比如会把goroutineA里面执行查询操作结果返回给goroutineB,导致问题
...
go goroutineA(conn)
go goroutineB(conn)
...

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

3. driver.Conn

Conn是数据库连接的接口定义,定义了一系列方法,且只能应用在一个goroutine里。

接口定义源码如下:

// Conn is a connection to a database. It is not used concurrently
// by multiple goroutines.
//
// Conn is assumed to be stateful.
type Conn interface {
	
    // 返回与当前连接相关的执行SQL语句的准备状态,可以进行查询、删除等操作
	Prepare(query string) (Stmt, error)

	// 关闭当前的链接,执行释放连接拥有的资源等清理工作
    // 因为驱动实现了database/sql里面建议的conn pool,所以不再去实现缓存 Conn操作,不然会引起问题 
	Close() error

	// 返回一个代表事务处理的Tx,通过它可以进行查询、更新等操作,或者对事务进行回滚、递交
	Begin() (Tx, error)
}

4. driver.Stmt

Stmt 是一种准备好的状态与 Conn 相关联,且只能应用于一个 goroutine中,不能应用于多个 goroutine中。相关源码如下:


// Stmt is a prepared statement. It is bound to a Conn and not
// used by multiple goroutines concurrently.
type Stmt interface {
	
    // 关闭当前的连接状态,但如果当前正在执行query,query还是会有效返回rows数据
	Close() error

	// 返回当前预留参数的个数,当返回>=0时,数据库驱动会智能检查调用者的参数。
    // 当数据库驱动包不知道预留参数的时候,返回-1
	NumInput() int

    // 执行Prepare准备好的SQL,传入参数执行Update/Insert等操作,返回Result数据
	Exec(args []Value) (Result, error)

	// 执行Prepare准备好的SQL,传入需要的参数执行select操作,返回Rows结果集
	Query(args []Value) (Rows, error)
}

5.driver.Tx

事务处理一般分两个过程,提交和回滚,对应源码如下:

// Tx is a transaction.
type Tx interface {

    // 提交事务
	Commit() error

    // 回滚事务
	Rollback() error
}

6.driver.Execer

这是一个Conn 可选择实现的接口,源码如下:

// 该接口是可选接口
// 如果该接口没有被定义,那么调用 DB.Exec,首先会调用 Prepare 返回Stmt,然后执行Stmt的Exec,最后
// 关闭Stmt.
type Execer interface {
    
    // 执行接口
	Exec(query string, args []Value) (Result, error)
}

7.driver.Result

该接口源码如下:

// 结果是查询执行的结果
type Result interface {

    // 返回由数据库执行插入操作得到的自增ID
	LastInsertId() (int64, error)

	// 返回query操作影响的数据条数
	RowsAffected() (int64, error)
}

8.driver.Rows

Rows是执行查询返回的结果集接口定义,源码如下:


// Rows is an iterator over an executed query's results.
type Rows interface {

	// 该函数返回查询数据库表的字段信息,这个返回的slice和SQL查询的字段一一对应,
    // 而不是返回整张表的所有字段。
	Columns() []string

	// 用来关闭Rows迭代器
	Close() error

	// 该函数用来返回下一条数据,把数据赋值给dest .
    // dest里面元素必须是driver.Value的值(string除外),返回的数据里面所有的 string 都必须转换成
    // []byte.如果最后没有数据了,Next 函数返回 io.EOF。
	Next(dest []Value) error
}

9.driver.RowsAffected

RowsAffected 是一个 int64 的别名,但它实现了Result接口,用来底层实现 Result 的表示方式。源码如下:


// RowsAffected implements Result for an INSERT or UPDATE operation
// which mutates a number of rows.
type RowsAffected int64

var _ Result = RowsAffected(0)

func (RowsAffected) LastInsertId() (int64, error) {
	return 0, errors.New("LastInsertId is not supported by this driver")
}

func (v RowsAffected) RowsAffected() (int64, error) {
	return int64(v), nil
}

10.driver.Value

Value 其实是一个空接口,可以容纳任何的数据。源码如下:

// diver 的 Value 是驱动必须能够操作的 Value,Value要么是nil,要么是下面任意一种:
//
//   int64
//   float64
//   bool
//   []byte
//   string   [*] 除了Rows.Next,返回的不能是string
//   time.Time
//
type Value interface{}

11.driver.ValueConverter

ValueConverter 接口定义了如何把一个普通的值转换成driver.Value的接口。源码如下:

// ValueConverter 接口定义了如何把一个普通的值转换成 driver.Value的接口
// 在开发的数据库驱动包里面实现这个接口的函数在很多地方会使用到,这个ValueConverter有很多好处:

// 1.转换 driver.value 到数据库表相应的字段,例如 int64 的数据如何转化成数据库表 uint16 字段。
// 2.把数据库查询结果转换成 driver.Value值
// 3.在scan函数里面如何把 driver.Value 值转换成用户定义的值。
type ValueConverter interface {
	// ConvertValue converts a value to a driver Value.
	ConvertValue(v interface{}) (Value, error)
}

12.driver.Valuer

源码如下:

// Valuer 接口定义了返回一个 driver.Value 的方式。
//
// 很多类型都实现了这个 Value 值,用于自身与 driver.Value的转化。
type Valuer interface {
	// Value returns a driver Value.
	// Value must not panic.
	Value() (Value, error)
}

三、结语

通过上面的讲解,读者对于驱动的开发有了初步理解。一个驱动只要实现了这些接口就可以完成增删改查等基本操作,剩余就是与相应的数据库进行数据交互等细节问题了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值