接口定义
Go没有内置的驱动支持任何的数据库,而是定义了database/sql接口,要用户基于驱动接口开发相应数据库的驱动。GitHub上有许多基于database/sql接口开发的不同数据库驱动,基于Go提供的接口标准来开发的驱动的好处就是当你要换驱动时,只需要导入相应的驱动,改一下打开的driverName和dataSourceName即可,其它的基本不需要动。MySql驱动比如github.com/go-sql-driver/mysql
其它数据库及驱动(来自astaxie/build-web-application-with-golang)
MySql
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写。
SQLite
https://github.com/mattn/go-sqlite3支持database/sql接口,基于cgo写的
https://github.com/feyeleanor/gosqlite3 不支持database/sql接口,基于cgo写的
https://github.com/phf/go-sqlite3 不支持database/sql接口,基于cgo写的
PostgreSQL
https://github.com/lib/pq 支持database/sql驱动,纯Go写的
https://github.com/jbarham/gopgsqldriver 支持database/sql驱动,纯Go写的
https://github.com/lxn/go-pgsql 支持database/sql驱动,纯Go写的
NOSQL数据库
redis
redis是一个key-value存储系统
https://github.com/garyburd/redigo (推荐)
https://github.com/go-redis/redis
https://github.com/hoisie/redis
https://github.com/alphazero/Go-Redis
https://github.com/simonz05/godis
mongoDB
MongoDB是一个高性能,开源,无模式的文档型数据库,是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
http://labix.org/mgo
在database/sql内部是通过map来存储不同驱动的,只需要调用sql.Register(name, driver)来注册相应的驱动
var (
driversMu sync.RWMutex
drivers = make(map[string]driver.Driver)
)
// 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
}github.com/go-sql-driver/mysql开发完MySQL驱动后,调用以下函数来注册驱动
func init() {
sql.Register("mysql", &MySQLDriver{})//注册MySQL驱动
}注册MySQL驱动是在init函数中,所以使用时需要导包,让其执行init方法,但又不使用,所以导入方式为import _ "github.com/go-sql-driver/mysql"
driver.Driver
driver.Driver是Go定义的一个驱动接口,只有一个Open方法,返回一个Conn接口
type Driver interface {
Open(name string) (Conn, error)
}MySQL驱动type MySQLDriver struct{}实现了driver.Driver接口返回了Conn接口,而MySQL的Conn的实现类型为mysql.mysqlContext
driver.Conn
Conn是一个数据库连接的接口定义,Conn只能应用在一个goroutine里面,不能使用在多个goroutine里面。
sql定义的接口
type Conn interface {
//返回与当前相关的执行sql语句的准备状态
Prepare(query string) (Stmt, error)
//关闭当前的连接
Close() error
//返回一个事务处理的Tx
Begin() (Tx, error)
}
MySQL实现类型
type mysqlConn struct {
buf buffer
netConn net.Conn
affectedRows uint64
insertId uint64
cfg *Config
maxAllowedPacket int
maxWriteSize int
writeTimeout time.Duration
flags clientFlag
status statusFlag
sequence uint8
parseTime bool
// for context support (Go 1.8+)
watching bool
watcher chan<- mysqlContext
closech chan struct{}
finished chan<- struct{}
canceled atomicError // set non-nil if conn is canceled
closed atomicBool // set when conn is closed, before closech is closed
}
driver.Stmt
Stmt是一种准备好的状态,和Conn相关联,而且只能应用于一个goroutine中,不能应用于多个goroutine。
sql定义的接口
type Stmt interface {
//关闭当前的链接状态
Close() error
//返回当前预留参数的个数
NumInput() int
//执行Prepare准备好的sql
Exec(args []Value) (Result, error)
//执行Prepare准备好的sql
Query(args []Value) (Rows, error)
}
MySQL实现类型
type mysqlStmt struct {
mc *mysqlConn
id uint32
paramCount int
}driver.Tx
事务处理一般就两个过程,递交或者回滚。
sql定义的接口
type Tx interface {
//递交事务
Commit() error
//回滚事务
Rollback() error
}
MySQL实现类型
type mysqlTx struct {
mc *mysqlConn
}driver.Result
这个是执行Update/Insert/Delete操作返回的结果接口定义
sql定义的接口
type Result interface {
//返回由数据库执行插入操作得到的自增ID号
LastInsertId() (int64, error)
//返回Update/Insert/Delete操作影响的数据条目数
RowsAffected() (int64, error)
}
MySQL实现类型
type mysqlResult struct {
affectedRows int64
insertId int64
}
driver.Rows
Rows是执行查询返回的结果集接口定义
sql定义的接口
type Rows interface {
//返回查询数据库表的字段信息
Columns() []string
//关闭Rows迭代器
Close() error
//返回下一条数据,把数据赋值给dest
Next(dest []Value) error
}
MySQL实现类型
type binaryRows struct {
mysqlRows
}database/sql
database/sql在database/sql/driver提供的接口基础上定义了一些更高阶的方法,用以简化数据库操作,同时内部还建议性地实现一个conn pool。
type DB struct {
connector driver.Connector
numClosed uint64
mu sync.Mutex // protects following fields
freeConn []*driverConn
connRequests map[uint64]chan connRequest
nextRequest uint64 // Next key to use in connRequests.
numOpen int // number of opened and pending open connections
openerCh chan struct{}
resetterCh chan *driverConn
closed bool
dep map[finalCloser]depSet
lastPut map[*driverConn]string // stacktrace of last conn's put; debug only
maxIdle int // zero means defaultMaxIdleConns; negative means 0
maxOpen int // <= 0 means unlimited
maxLifetime time.Duration // maximum amount of time a connection may be reused
cleanerCh chan struct{}
stop func() // stop cancels the connection opener and the session resetter.
}我们可以看到Open函数返回的是DB对象,里面有一个freeConn,它就是那个简易的连接池。它的实现相当简单或者说简陋,就是当执行db.prepare -> db.prepareDC的时候会defer dc.releaseConn,然后调用db.putConn,也就是把这个连接放入连接池,每次调用db.conn的时候会先判断freeConn的长度是否大于0,大于0说明有可以复用的conn,直接拿出来用就是了,如果不大于0,则创建一个conn,然后再返回之。
使用
使用github.com/go-sql-driver/mysql驱动
导包
import (
"database/sql"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
"fmt"
)初始化
注意:要想解析time.Time类型,必须要设置parseTime=True
var DB *sql.DB
func init() {
var err error
DB,err = sql.Open("mysql","root:root@tcp(localhost:3306)/cgo?charset=utf8&parseTime=True&loc=Local")
if err != nil{
log.Panic(err)
}
DB.SetMaxOpenConns(30)
DB.SetMaxIdleConns(10)
err = DB.Ping()
if err != nil{
log.Panic(err)
}
}插入数据
func insert() int64 {
result,err := DB.Exec("INSERT INTO user(`username`,`password`,`create_time`) VALUES (?,?,?)","user_1234","1234",time.Now())
if err != nil{
log.Panic(err)
}
id,err := result.LastInsertId()
if err != nil{
log.Panic(err)
}
return id
}插入数据方式2
func insert2() int64 {
stmt,err := DB.Prepare("INSERT INTO user(`username`,`password`,`create_time`) VALUES(?,?,?)")
if err != nil {
log.Panic(err)
}
result,err := stmt.Exec("user_1111","1111",time.Now())
if err != nil{
log.Panic(err)
}
id,err := result.LastInsertId()
if err != nil{
log.Panic(err)
}
fmt.Println("id",id)
num,err := result.RowsAffected()
if err != nil{
log.Panic(err)
}
fmt.Println("num",num)
stmt.Close()
return id
}更新数据
func update() int64{
result,err := DB.Exec("UPDATE user SET `password` = ? WHERE id = ?","5678",3)
if err != nil {
log.Panic(err)
}
num,err := result.RowsAffected()
if err != nil{
log.Panic(err)
}
return num
}删除数据
func delete() int64 {
result,err := DB.Exec("DELETE FROM user WHERE id = ?",3)
if err != nil{
log.Println(err)
}
num,err := result.RowsAffected()
if err != nil{
log.Panic(err)
}
return num
}查询单条数据
type MUser struct {
ID int64
Username string
Password string
CreateTime time.Time
}
func query() *MUser {
var user MUser
err := DB.QueryRow("SELECT * FROM user WHERE id = ?",3).Scan(&user.ID,&user.Username,&user.Password,&user.CreateTime)
if err != nil {
//如果查询不到结果报错panic: sql: no rows in result set
log.Panic(err)
}
return &user
}查询多条数据
func querys() []*MUser {
var users []*MUser
rows,err := DB.Query("SELECT * FROM user")
if err != nil{
log.Panic(err)
}
for rows.Next(){
var user MUser
err := rows.Scan(&user.ID,&user.Username,&user.Password,&user.CreateTime)
if err != nil {
continue
}
users = append(users,&user)
}
rows.Close()
return users
}事务
func transaction() {
tx,err := DB.Begin()
if err != nil{
log.Panic(err)
}
result,err := tx.Exec("INSERT INTO user(`username`,`password`,`create_time`) VALUES(?,?,?)","user_1234","1234",time.Now())
if err != nil{
log.Panic(err)
}
id,err := result.LastInsertId()
if err != nil{
log.Panic(err)
}
result,err = tx.Exec("UPDATE user SET `password` = ? WHERE id = ?","5678",id)
if err != nil{
tx.Rollback()
log.Panic(err)
}
id,err = result.LastInsertId()
if err != nil{
log.Panic(err)
}
fmt.Println("id",id)
num,err := result.RowsAffected()
if err != nil{
log.Panic(err)
}
fmt.Println("num",num)
tx.Commit()
}参考:https://github.com/astaxie/build-web-application-with-golang