Go语言 Web开发(5)数据库标准接口解析


Go没有官方提供的数据库驱动,而是定义了一些数据库驱动标准接口,这样我们可以根据定义的接口来开发相应的数据库驱动,后期进行数据库转移时就不需要进行修改。

我们来看看Go有哪些数据库标准接口
①sql.Register
在开发数据库驱动时,都会实现init函数,然后在init函数中调用sql的Register函数完成驱动的注册。
sql的Register函数源码

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
}

使用init函数调用sql的Register函数完成驱动的注册示例

示例一

func init() {
sql.Register("sqlite3", &SQLiteDriver{})
}

实列二

var d = Driver{proto: "tcp", raddr: "127.0.0.1:3306"}
func init() {
Register("SET NAMES utf8")
sql.Register("mymysql", &d)
}

所有注册过的数据库驱动都会存储在一个map中

drivers   = make(map[string]driver.Driver)

②driver.Driver
这也是一个数据库驱动标准接口,关于它的源码说明如下:

type Driver interface {
   // Open返回与数据库的新连接。
   //名称是特定于驱动程序的格式的字符串。
   // Open可以返回一个缓存连接(一个先前已关闭),但这样做是不必要的; sql包维护一个空闲连接池,以便有效地重用。
   //返回的连接一次只能由一个goroutine使用。
   Open(name string) (Conn, error)
}

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

③driver.Conn
这是一个数据库连接的接口,只能应用在一个goroutine里面,不能使用在多个goroutine里面,它定义的一些方法如下所示:

// Conn是与数据库的连接。 它不是由多个goroutines同时使用的。
// Conn被认为是有状态的。
type Conn interface {
    // 返回与当前连接相关的执行Sql语句的准备状态,可以进行查询、删除等操作。
   Prepare(query string) (Stmt, error)

    //关闭当前的连接并可能停止任何当前准备好的语句和事务,将此连接标记为不再使用。
    //因为sql包维护一个空闲的连接池,并且只有在空闲连接过剩时才调用Close,所以驱动程序不需要进行自己的连接缓存。
   Close() error

    //返回一个代表事务处理的Tx,通过它你可以进行查询,更新等操作,或者对事务进行回滚、递交。
    //不推荐使用:驱动程序应该实现ConnBeginTx(或者另外)。
   Begin() (Tx, error)
}

④driver.Stmt
关于它的源码解释如下所示:

// Stmt是一个准备好的声明。 这是一个康恩而不是
//同时使用多个goroutines。
type Stmt interface {
    //关闭语句。
    //从Go 1.1开始,如果Stmt正在被任何查询使用,它将不会被关闭。
   Close() error

    // NumInput返回占位符参数的数量。
    //如果NumInput返回> = 0,则在调用语句的Exec或Query方法之前,sql包将从调用者检查参数计数并将错误返回给调用者。
    //如果驱动程序不知道占位符数,NumInput也可能返回-1。 在这种情况下,sql包将无法检查Exec或Query参数计数。
   NumInput() int

    // Exec执行不返回行的查询,例如INSERT或UPDATE。
    //不推荐使用:驱动程序应该实现StmtExecContext(或另外)。
   Exec(args []Value) (Result, error)

    // Query执行可能返回行的查询,例如SELECT。
    //不推荐使用:驱动程序应该实现StmtQueryContext(或另外)。
   Query(args []Value) (Rows, error)
}

⑤driver.Tx
事务处理一般就两个过程,递交或者回滚。数据库驱动里面也只需要实现这两个函数就可以

type Tx interface {
   Commit() error //递交一个事务
   Rollback() error //用来回滚事务
}

⑥driver.Execer
作为Conn可选择实现的一个接口,关于它的源码解释如下所示

// Execer是一个可选的接口,可以由Conn实现。
//如果Conn既不实现ExecerContext也不实现Execer Execer,则sql包的DB.Exec将首先准备一个查询,执行该语句,然后关闭该语句。
// Exec可能会返回ErrSkip。
//不推荐使用:驱动程序应该实现ExecerContext。
type Execer interface {
   Exec(query string, args []Value) (Result, error)
}

⑦driver.Result
这个是执行Update/Insert等操作返回的结果接口定义,关于它的源码解释如下:

//Result是查询执行的结果接口。
type Result interface {
   // LastInsertId返回数据库的自动生成的ID,例如,INSERT到具有主键的表中。
   LastInsertId() (int64, error)

   // RowsAffected返回受查询影响的行数。
   RowsAffected() (int64, error)
}

⑧driver.Rows
Rows是执行查询返回的结果集接口定义,关于它的源码解释如下:

// Rows是执行查询结果的迭代器。
type Rows interface {
   //返回列的名称。 从切片的长度推断结果的列数。 如果未知特定列名称,则应为该条目返回空字符串。
   Columns() []string

    // Close关闭行迭代器。
   Close() error
    
  //调用Next以将下一行数据填充到提供的切片中。 提供的切片与Columns()的宽度相同。
  //当没有更多行时,Next应该返回io.EOF。
  //不应将dest写入Next之外。 关闭行时应小心,不要修改dest中保存的缓冲区。
   Next(dest []Value) error
}

⑨driver.Value
Value其实就是一个空接口,他可以容纳任何的数据

//值是驱动程序必须能够处理的值。
//它可以是nil,由数据库驱动程序的Name Value Checker接口处理的类型,或者是以下类型之一的实例:
//int64,float64,bool,[]byte,string,time.Time
type Value interface{}

⑩driver.RowsAffected
RowsAffested其实就是一个int64的别名,但是他实现了Result接口,用来底层实现Result的表示方式

// RowsAffected实现INSERT或UPDATE操作的结果,该操作会改变多个行。
type RowsAffected int64

var _ Result = RowsAffected(0)

func (RowsAffected) LastInsertId() (int64, error) {
   return 0, errors.New("no LastInsertId available")
}

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

11 driver.ValueConverter
ValueConverter接口定义了如何把一个普通的值转化成driver.Value的接口

// ValueConverter是提供ConvertValue方法的接口。
//驱动程序包提供了ValueConverter的各种实现,以提供驱动程序之间转换的一致实现。 ValueConverters有几个用途:
// *从sql包提供的Value类型转换为数据库表的特定列类型并确保它适合,例如确保特定的int64适合表的uint16列。
// *将数据库中给定的值转换为其中一个驱动值类型。
// *由sql包,用于从驱动程序的Value类型转换为扫描中的用户类型。
type ValueConverter interface {
   // ConvertValue将值转换为驱动程序值。
   ConvertValue(v interface{}) (Value, error)
}

12 driver.Valuer
Valuer接口定义了返回一个driver.Value的方式

// Valuer是提供Value方法的接口。
//实现Valuer接口的类型能够将自身转换为驱动程序值。
type Valuer interface {
    // Value返回驱动程序值。
   Value() (Value, error)
}

参考书籍:《Go Web编程》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容