Go与PHP不同的地方是Go官方没有提供数据库驱动,而是为开发数据库驱动定义了一些标准接口,开发者可以根据定义的接口来开发相应的数据库驱动,这样做有一个好处,只要是按照标准接口开发的代码, 以后需要迁移数据库时,不需要任何修改。那么Go都定义了哪些标准接口呢?让我们来详细的分析一下

sql.Register

这个存在于database/sql的函数是用来注册数据库驱动的,当第三方开发者开发数据库驱动时,都会实现init函数,在init里面会调用这个Register(name string, driver driver.Driver)完成本驱动的注册。

我们来看一下mymysql、sqlite3的驱动里面都是怎么调用的:

//https://github.com/mattn/go-sqlite3驱动
func init() {sql.Register("sqlite3", &SQLiteDriver{})
}//https://github.com/mikespook/mymysql驱动
// 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)
}

我们看到第三方数据库驱动都是通过调用这个函数来注册自己的数据库驱动名称以及相应的driver实现。在database/sql内部通过一个map来存储用户定义的相应驱动。

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

因此通过database/sql的注册函数可以同时注册多个数据库驱动,只要不重复。

在我们使用database/sql接口和第三方库的时候经常看到如下:

  import ("database/sql"_ "github.com/mattn/go-sqlite3")

新手都会被这个_所迷惑,其实这个就是Go设计的巧妙之处,我们在变量赋值的时候经常看到这个符号,它是用来忽略变量赋值的占位符,那么包引入用到这个符号也是相似的作用,这儿使用_的意思是引入后面的包名而不直接使用这个包中定义的函数,变量等资源。

我们在2.3节流程和函数一节中介绍过init函数的初始化过程,包在引入的时候会自动调用包的init函数以完成对包的初始化。因此,我们引入上面的数据库驱动包之后会自动去调用init函数,然后在init函数里面注册这个数据库驱动,这样我们就可以在接下来的代码中直接使用这个数据库驱动了。

driver.Driver

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

type Driver interface {Open(name string) (Conn, error)
}

返回的Conn只能用来进行一次goroutine的操作,也就是说不能把这个Conn应用于Go的多个goroutine里面。如下代码会出现错误

...
go goroutineA (Conn)  //执行查询操作
go goroutineB (Conn)  //执行插入操作
...

上面这样的代码可能会使Go不知道某个操作究竟是由哪个goroutine发起的,从而导致数据混乱,比如可能会把goroutineA里面执行的查询操作的结果返回给goroutineB从而使B错误地把此结果当成自己执行的插入数据。

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

driver.Conn

Conn是一个数据库连接的接口定义,他定义了一系列方法,这个Conn只能应用在一个goroutine里面,不能使用在多个goroutine里面,详情请参考上面的说明。

type Conn interface {Prepare(query string) (Stmt, error)Close() errorBegin() (Tx, error)
}

Prepare函数返回与当前连接相关的执行Sql语句的准备状态,可以进行查询、删除等操作。

Close函数关闭当前的连接,执行释放连接拥有的资源等清理工作。因为驱动实现了database/sql里面建议的conn pool,所以你不用再去实现缓存conn之类的,这样会容易引起问题。

Begin函数返回一个代表事务处理的Tx,通过它你可以进行查询,更新等操作,或者对事务进行回滚、递交。

driver.Stmt

Stmt是一种准备好的状态,和Conn相关联,而且只能应用于一个goroutine中,不能应用于多个goroutine。

type Stmt interface {Close() errorNumInput() intExec(args []Value) (Result, error)Query(args []Value) (Rows, error)
}

Close函数关闭当前的链接状态,但是如果当前正在执行query,query还是有效返回rows数据。

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

Exec函数执行Prepare准备好的sql,传入参数执行update/insert等操作,返回Result数据

Query函数执行Prepare准备好的sql,传入需要的参数执行select操作,返回Rows结果集

driver.Tx

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

type Tx interface {Commit() errorRollback() error
}

这两个函数一个用来递交一个事务,一个用来回滚事务。

driver.Execer

这是一个Conn可选择实现的接口

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函数返回query操作影响的数据条目数。

driver.Rows

Rows是执行查询返回的结果集接口定义

type Rows interface {Columns() []stringClose() errorNext(dest []Value) error
}

Columns函数返回查询数据库表的字段信息,这个返回的slice和sql查询的字段一一对应,而不是返回整个表的所有字段。

Close函数用来关闭Rows迭代器。

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

driver.RowsAffected

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

type RowsAffected int64func (RowsAffected) LastInsertId() (int64, error)func (v RowsAffected) RowsAffected() (int64, error)

driver.Value

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

type Value interface{}

drive的Value是驱动必须能够操作的Value,Value要么是nil,要么是下面的任意一种

int64
float64
bool
[]byte
string   [*]除了Rows.Next返回的不能是string.
time.Time

driver.ValueConverter

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

type ValueConverter interface {ConvertValue(v interface{}) (Value, error)
}

在开发的数据库驱动包里面实现这个接口的函数在很多地方会使用到,这个ValueConverter有很多好处:

  • 转化driver.value到数据库表相应的字段,例如int64的数据如何转化成数据库表uint16字段
  • 把数据库查询结果转化成driver.Value值
  • 在scan函数里面如何把driver.Value值转化成用户定义的值

driver.Valuer

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

type Valuer interface {Value() (Value, error)
}

很多类型都实现了这个Value方法,用来自身与driver.Value的转化。

通过上面的讲解,你应该对于驱动的开发有了一个基本的了解,一个驱动只要实现了这些接口就能完成增删查改等基本操作了,剩下的就是与相应的数据库进行数据交互等细节问题了,在此不再赘述。

database/sql

database/sql在database/sql/driver提供的接口基础上定义了一些更高阶的方法,用以简化数据库操作,同时内部还建议性地实现一个conn pool。

type DB struct {driver   driver.Driverdsn      stringmu       sync.Mutex // protects freeConn and closedfreeConn []driver.Connclosed   bool
}

我们可以看到Open函数返回的是DB对象,里面有一个freeConn,它就是那个简易的连接池。它的实现相当简单或者说简陋,就是当执行Db.prepare的时候会defer db.putConn(ci, err),也就是把这个连接放入连接池,每次调用conn的时候会先判断freeConn的长度是否大于0,大于0说明有可以复用的conn,直接拿出来用就是了,如果不大于0,则创建一个conn,然后再返回之。

go中的database/sql接口相关推荐

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

    目录 一 .引言 1.1 概述 1.2 包接口 二.database/sql接口(常用) 1.sql.Register 2.driver.Driver 3. driver.Conn 4. driver ...

  2. oracle 在此 select 语句中缺少 into 子句,Go database/sql文档

    No.1 文档概要 在Golang中使用SQL或类似SQL的数据库的惯用方法是通过 database/sql 包操作.它为面向行的数据库提供了轻量级的接口.这篇文章是关于如何使用它,最常见的参考. 为 ...

  3. mysql pmt函数怎么用_在Golang中如何正确地使用database/sql包访问数据库

    本文记录了我在实际工作中关于数据库操作上一些小经验,也是新手入门golang时我认为一定会碰到问题,没有什么高大上的东西,所以希望能抛砖引玉,也算是对这个问题的一次总结.其实我也是一个新手,机缘巧合几 ...

  4. sql语句和java的关系_java中Statement 与 PreparedStatement接口之间的关系和区别

    Statement 和 PreparedStatement之间的关系和区别. 关系:PreparedStatement继承自Statement,都是接口 区别:PreparedStatement可以使 ...

  5. golang database/sql包 简介

    概述 sql.DB不是一个连接,它是数据库的抽象接口.它可以根据driver打开关闭数据库连接,管理连接池.正在使用的连接被标记为繁忙,用完后回到连接池等待下次使用.所以,如果你没有把连接释放回连接池 ...

  6. 【Qt】数据库SQL接口层

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. QSqlDatabase类 04. QSqlError类 05. QSqlQuery类 06. QSqlRecord类 0 ...

  7. sql中截取字符串函数_SQL Server 2017中的顶级SQL字符串函数

    sql中截取字符串函数 SQL Server 2017 has been in the talk for its many features that simplify a developer's l ...

  8. database/sql

    官方database/sql包介绍 介绍 在Go中访问数据库,需要使用sql.DB类型用于创建语句和事务,执行查询和获取结果 一个sql.DB不是一个数据库连接,它是一个数据库的抽象 sql.DB通过 ...

  9. 【旧文重新排版】借 Go 语言 database/sql 包谈数据库驱动和连接池设计

    文 | jiayangchen 封面图片 | Unsplash 因为之前的文章感觉排版太差了,不容易阅读 接下来会把一些旧文重新排版再发一下 即使你不了解 Go 语言,阅读本文也不会有障碍 1. 什么 ...

最新文章

  1. 基于spring boot调用接口的工具类
  2. 我的自我介绍以及决心书
  3. 2015 跨年博文总结
  4. linux配置网卡自动获取的命令,linux 命令行下配置网卡自动获取 IP
  5. python编程基础知识点_12个关于Python编程基础知识的总结
  6. 18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请问你是怎么调度的咩
  7. python内核死亡的原因_Kernel Panic常见原因以及解决方法
  8. .NET中Redis安装部署及使用方法
  9. 矩池云怎么上传文件夹
  10. ---转载---phython资料
  11. 悼念512汶川大地震遇难同胞
  12. phyton的函数与类的学习
  13. css 属性 position:sticky (粘滞的) 制作导航吸顶效果
  14. 软件工程中国学科排名——2021软科
  15. 安全管家2012上半年手机安全分析报告
  16. PAT乙级—1053. 住房空置率 (20)-native
  17. 笔记本电脑在拔插电源时屏幕会黑一下,然后马上恢复正常
  18. 信息系统项目管理师 第七章-项目成本管理
  19. Python——飞机大战
  20. 通过 汇编了解C语言 指针 悬垂指针概念

热门文章

  1. 【Android 组件化】路由组件 ( 生成 Root 类记录模块中的路由表 )
  2. 【Android 安装包优化】使用 lib7zr.so 动态库处理压缩文件 ( 测试 lib7zr.so 动态库调用 )
  3. 【组合数学】组合恒等式 ( 变上项求和 1 组合恒等式 | 三种组合恒等式证明方法总结 | 证明变上项求和 1 组合恒等式 )
  4. 【Kotlin】扩展函数作用域分析 ( 扩展函数导入 | 扩展函数重载 | 扩展函数作用域优先级 )
  5. 【 Markdown 】Markdown 编辑器语法常用格式 ( 整理中 ... )
  6. OpenCV+Qt+CMake安装+十种踩坑
  7. 3组现代软件工程第三次作业
  8. 利用Aspose.Word控件和Aspose.Cell控件,实现Word文档和Excel文档的模板化导出
  9. javase-NIO
  10. UVA-10054 The Necklace (欧拉回路)