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

目前NOSQL已经成为Web开发的一个潮流,很多应用采用了NOSQL作为数据库,而不是以前的缓存,后面将介绍MongoDB和Redis两种NOSQL数据库。

详细分析一下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"
)

这儿使用_的意思是引入后面的包名而不直接使用这个包中定义的函数,变量等资源。

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

driver.Driver

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

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

返回的Conn只能用来进行一次goroutine的操作,也就是说不能把这个Conn应用于Go的多个goroutine里面。

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

driver.Conn

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

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

Prepare函数返回与当前连接相关的执行Sql语句的准备状态,可以进行查询、删除等操作。
Close函数关闭当前的连接,执行释放连接拥有的资源等清理工作。因为驱动实现了database/sql里面建议的conn pool,所以你不用再去实现缓存conn之类的,这样会容易引起问题。
Begin函数返回一个代表事务处理的Tx,通过它你可以进行查询,更新等操作,或者对事务进行回滚、递交。

driver.Stmt

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

type Stmt interface {
Close() error
NumInput() int
Exec(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() error
Rollback() error
}

driver.Execer

这是一个Conn可选择实现的接口。如果这个接口没有定义,那么在调用DB.Exec,就会首先调用Prepare返回Stmt,然后执行Stmt的Exec,然后关闭Stmt。

type Execer interface {
Exec(query string, args []Value) (Result, error)
}

driver.Result

这个是执行Update/Insert等操作返回的结果接口定义

type Result interface {
LastInsertId() (int64, error)
RowsAffected() (int64, error)
}

LastInsertId函数返回由数据库执行插入操作得到的自增ID号。
RowsAffected函数返回query操作影响的数据条目数。

driver.Rows

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

type Rows interface {
Columns() []string
Close() error
Next(dest []Value) error
}

Columns函数返回查询数据库表的字段信息,这个返回的slice和sql查询的字段一一对应,而不是返回整个表的所有字段。
Close函数用来关闭Rows迭代器。
Next函数用来返回下一条数据,把数据赋值给dest。dest里面的元素必须是driver.Value的值除了string,返回的数据里面所有的string都必须要转换成[]byte。如果最后没数据了,Next函数最后返回io.EOF。

driver.RowsAffected

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

type RowsAffected int64
func (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 、time.Time

driver.ValueConverter

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

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

在开发的数据库驱动包里面实现这个接口的函数在很多地方会使用到,这个ValueConverter有很多好处:
转化driver.value到数据库表相应的字段,例如int64的数据如何转化成数据库表uint16字段
把数据库查询结果转化成driver.Value值
在scan函数里面如何把dirve.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.Driver
dsn string
mu sync.Mutex // protects freeConn and closed
freeConn []driver.Conn
closed bool
}

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

MySQL驱动

Go中支持MySQL的驱动目前比较多,有如下几种,有些是支持database/sql标准,而有些是采用了自己的实现接口,常用的有如下几种:
https://github.com/Go-SQL-Driver/MySQL 支持database/sql,全部采用go写。
https://github.com/ziutek/mymysql 支持database/sql,也支持自定义的接口,全部采用go写。
https://github.com/Philio/GoMySQL 不支持database/sql,自定义接口,全部采用go写。
接下来的例子以第一个驱动为例:

package main

import (
"database/sql"
"fmt"
_ "github.com/Go-SQL-Driver/MySQL"
) func main() {
connection, err := sql.Open("mysql", "aries1991:admin123@/aries?charset=utf8")
panicErr(err)
//插入数据
stmt, err := connection.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
panicErr(err)
res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
panicErr(err)
id, err := res.LastInsertId()
panicErr(err)
fmt.Println(id)
//查询数据
rows, err := connection.Query("SELECT * FROM userinfo")
panicErr(err)
for rows.Next() {
var uid int
var username string
var department string
var created string
err = rows.Scan(&uid, &username, &department, &created)
panicErr(err)
fmt.Println(uid, username, department, created)
} connection.Close()
} func panicErr(err error) {
if err != nil {
panic(err)
}
}

解释一下关键几个函数:
sql.Open() 函数用来打开一个注册过的数据库驱动,Go-MySQL-Driver中注册了mysql这个数据库驱动,

第二个参数是DNS(Data Source Name),它是Go-MySQL-Driver定义的一些数据库链接和配置信息。支持如下格式:
user@unix(/path/to/socket)/dbname?charset=utf8

user:password@tcp(localhost:5555)/dbname?charset=utf8

user:password@/dbname

user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname

db.Prepare()函数用来返回准备要执行的sql操作,然后返回准备完毕的执行状态。

db.Query()函数用来直接执行Sql返回Rows结果。

stmt.Exec()函数用来执行stmt准备好的SQL语句

NOSQL数据库操作

redis

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集合)。
目前应用redis最广泛的应该是新浪微博平台,其次还有Facebook收购的图片社交网站instagram。以及其他一些有名的互联网企业。

Go目前支持redis的驱动有如下 :

https://github.com/alphazero/Go-Redis

、http://code.google.com/p/tideland-rdc、https://github.com/simonz05/godis、https://github.com/astaxie/goredis

//TODO

#笔记内容来自 《Go Web编程》

GO.数据库接口的更多相关文章

  1. 9、Qt Project之简单的数据库接口

    简单的数据库接口  Step1:首先完成整个UI界面的额设计: <?xml version="1.0" encoding="UTF-8"?> < ...

  2. 【Mybatis】【1】generate批量生成实体类,数据库接口类和mapper

    前言: 1,实体类之类如果自己写的话,比较繁琐,还容易出错,所以用generate自动生成 2,int类型可能会生成为short类型,建议不要手动改回int类.因为下次生成又是short类型了,可能会 ...

  3. [py]django强悍的数据库接口(QuerySet API)-增删改查

    django强悍的数据库接口(QuerySet API) 4种方法插入数据 获取某个对象 filter过滤符合条件的对象 filter过滤排除某条件的对象- 支持链式多重查询 没找到排序的 - 4种方 ...

  4. Python与数据库[1] -> 数据库接口/DB-API[0] -> 通用标准

    数据库接口 / DB-API 在Python中,数据库是通过适配器(Adaptor)来连接访问数据库的,适配器通常与数据库客户端接口(通常为C语言编写)想连接,而不同的适配器都会尽量满足相同的DB-A ...

  5. Java se课程设计详解——数据库接口类(1)

    开始做课程设计的时候根本无从下手,后来查阅资料后发现是先从数据库开始的.整个课程设计需要用到的如下图,今天总结一下数据库接口! 数据库接口需要用到两个类,一个是DAO.java,另一个是propert ...

  6. 泛微OA e-cology 数据库接口信息泄露学习

    泛微OA e-cology 数据库接口信息泄露 漏洞信息 攻击者可通过存在漏洞的页面直接获取到数据库配置信息.如果攻击者可直接访问数据库,则可直接获取用户数据,甚至可以直接控制数据库服务器:会将当前连 ...

  7. c语言下的通用数据库接口(之sqlite消化,模拟c#,java的反射)

    在java/C#中都有类的反射,而C下是不存在的. java/C#中能够把表设计成类.而C下仅仅能设计成结构体形式. 在java中有hibernate来操作数据库,可是在C以下怎么设计好呢? 如今,我 ...

  8. 基于SpringMVC+Spring+MyBatis实现秒杀系统【数据库接口】

    前言 该篇教程主要关注MyBatis实现底层的接口,把MyBatis交给Spring来托管.数据库连接池用的c3p0.数据库用的MySQL.主要有2个大类:秒杀商品的查询.秒杀明细的插入. 准备工作 ...

  9. Python与数据库[1] -> 数据库接口/DB-API[1] -> MySQL 适配器

    MySQL适配器 / MySQL Adapter MySQL是一种关系型数据库,下面主要介绍利用如何利用Python的MySQL适配器来对MySQL进行操作,其余内容可参考文末相关阅读. 1 MySQ ...

  10. Python与数据库[1] -> 数据库接口/DB-API[2] -> SQL Server 适配器

    SQL_Server适配器 / SQL_Server Adapter 1 环境配置 / Environment Configuration 安装SQL_Server的Python适配器包 pip in ...

随机推荐

  1. 【iOS】tableView:cellForRowAtIndexPath: 方法未调用

    今天遇到这个问题, UITableView 的代理方法 tableView:cellForRowAtIndexPath: - (UITableViewCell *)tableView:(UITable ...

  2. Android Studio项目/Flutter 案例中Gradle报错通用解决方案(包括Unable to tunnel through proxy问题)

    目录 Step 1:修改Gradle版本为本地版本 Step 2:修改classpath为Android Studio版本 Step 3:关闭代理 Step 1:修改Gradle版本为本地版本     ...

  3. kubernetes对接第三方认证

    kubernetes对接第三方认证 kubernetes离线安装包地址 概述 本文介绍如何使用github账户去关联自己kubernetes账户.达到如下效果: 使用github用户email作为ku ...

  4. Permission 使用详解

    极力推荐文章:欢迎收藏 Android 干货分享 阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以 ...

  5. Canvas动画(PC端 移动端)

    Canvas动画(PC端 移动端) 一,介绍与需求 1.1,介绍 canvas是HTML5中新增一个HTML5标签与操作canvas的javascript API,它可以实现在网页中完成动态的2D与3 ...

  6. 深入剖析 RabbitMQ —— Spring 框架下实现 AMQP 高级消息队列协议

    前言 消息队列在现今数据量超大,并发量超高的系统中是十分常用的.本文将会对现时最常用到的几款消息队列框架 ActiveMQ.RabbitMQ.Kafka 进行分析对比.详细介绍 RabbitMQ 在 ...

  7. 10、二维数组的申请(test7.java)

    我个人认为,二维数组的构造就是在一位数组中存入一个地址,这个地址指向另一个一位数组,这样通过这种排列组合便构造成了二维数组. 二维数组的形状,有的时候二维数组看起来像是一个矩阵,所以一般情况下如果涉及 ...

  8. 牛客多校训练第八场G.Gemstones(栈模拟)

    题目传送门 题意: 输入一段字符串,字符串中连续的三个相同的字符可以消去,消去后剩下的左右两段字符串拼接,求最多可消去次数. 输入:ATCCCTTG   输出:2 ATCCCTTG(消去CCC)——& ...

  9. Powered by .NET Core 进展:用 docker-compose 验证高并发问题嫌疑犯 docker swarm

    相关博文: [故障公告]发布 .NET Core 版博客站点引起大量 500 错误 [网站公告].NET Core 版博客站点第二次发布尝试 暴风雨中的 online : .NET Core 版博客站 ...

  10. 页面性能监控之performance

    页面性能监测之performance author: @TiffanysBear 最近,需要对业务上的一些性能做一些优化,比如降低首屏时间.减少核心按钮可操作时间等的一些操作:在这之前,需要建立的就是 ...