Go操作MySQL之database/sql
这种方式是Go内置的操作MySQL的方式,有点复杂,平时很少使用这里只做大概介绍,了解下即可。
连接
Go语言中的database/sql包提供了保证SQL或类SQL数据库的泛用接口,并不提供具体的数据库驱动。使用database/sql包时必须注入(至少)一个数据库驱动。
我们常用的数据库基本上都有完整的第三方实现。
下载依赖
go get -u github.com/go-sql-driver/mysql
使用MySQL驱动
func Open(driverName, dataSourceName string) (*DB, error)
Open打开一个dirverName指定的数据库,dataSourceName指定数据源,一般包至少括数据库文件名和(可能的)连接信息。
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// DSN:Data Source Name
dsn := "user:password@tcp(127.0.0.1:3306)/dbname"
db, err := sql.Open("mysql", dsn)
if err != nil {
panic(err)
}
defer db.Close()
}
初始化连接
Open函数可能只是验证其参数,而不创建与数据库的连接。如果要检查数据源的名称是否合法,应调用返回值的Ping方法。
返回的DB可以安全的被多个goroutine同时使用,并会维护自身的闲置连接池。这样一来,Open函数只需调用一次。很少需要关闭DB。
// 定义一个全局对象db
var db *sql.DB
// 定义一个初始化数据库的函数
func initDB() (err error) {
// DSN:Data Source Name
dsn := "user:password@tcp(127.0.0.1:3306)/test"
// 不会校验账号密码是否正确
db, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
// 尝试与数据库建立连接(校验dsn是否正确)
err = db.Ping()
if err != nil {
return err
}
return nil
}
func main() {
err := initDB() // 调用输出化数据库的函数
if err != nil {
fmt.Printf("init db failed,err:%v\n", err)
return
}
}
View Code
其中sql.DB是一个数据库(操作)句柄,代表一个具有零到多个底层连接的连接池。它可以安全的被多个go程同时使用。database/sql包会自动创建和释放连接;它也会维护一个闲置连接的连接池。
SetMaxOpenConns
func (db *DB) SetMaxOpenConns(n int)
SetMaxOpenConns设置与数据库建立连接的最大数目。 如果n大于0且小于最大闲置连接数,会将最大闲置连接数减小到匹配最大开启连接数的限制。 如果n<=0,不会限制最大开启连接数,默认为0(无限制)。
SetMaxIdleConns
func (db *DB) SetMaxIdleConns(n int)
SetMaxIdleConns设置连接池中的最大闲置连接数。 如果n大于最大开启连接数,则新的最大闲置连接数会减小到匹配最大开启连接数的限制。 如果n<=0,不会保留闲置连接。
建库建表
我们先在MySQL中创建一个名为sql_test的数据库
create database sql_test character set utf8;
进入该数据库:
use sql_test;
执行以下命令创建一张用于测试的数据表:
CREATE TABLE `user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT '',
`age` INT(11) DEFAULT '0',
PRIMARY KEY(`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
往表中插入三条数据用于实验
insert into user(name, age) values ("老宋", "30"), ("老王", "35"), ("富贵", "25");
CRUD
package main
// database/sql 连接MySQL示例代码
import (
"database/sql"
"fmt"
// 只用到了它这个包里面的init()
_ "github.com/go-sql-driver/mysql"
)
// 下载驱动:go get -u github.com/go-sql-driver/mysql
func main() {
// dsn:"user:password@tcp(ip:port)/databasename"
dsn := "root:root@tcp(127.0.0.1:3306)/sql_test"
// 调用标准库中的方法
// 前提是要注册对应数据库的驱动
db, err := sql.Open("mysql", dsn)
if err != nil {
fmt.Printf("open mysql failed, err:%v\n", err)
return
}
defer db.Close()
// 尝试链接一下数据库,校验用户名密码是否正确...
err = db.Ping()
if err != nil {
fmt.Printf("connect MySQL failed, err:%v\n", err)
return
}
fmt.Println("连接数据库成功!")
}
连接数据库
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// 使用连接池方式连接MySQL
// DB 数据库连接句柄,全局变量
var DB *sql.DB
func initDB(dsn string) (err error) {
DB, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
err = DB.Ping()
if err != nil {
return err
}
fmt.Println("连接数据库成功!")
// 连接上数据库了
// 设置最大连接数
DB.SetMaxOpenConns(50)
// 设置最大的空闲连接数
// DB.SetMaxIdleConns(20)
return nil
}
// User 用户结构体
type User struct {
id int
name string
age string
}
// 查询单条
func queryRowDemo() {
// 查询单行数据
// sql:select id,name,age from user where id=1;
var user User
sqlStr := "select id,name,age from user where id=1"
err := DB.QueryRow(sqlStr).Scan(&user.id, &user.name, &user.age)
if err != nil {
fmt.Printf("query failed, err:%v\n", err)
return
}
fmt.Printf("查询结果:%#v\n", user)
}
// 演示查询单行数据不调用row的Scan方法,会一直占用连接
func queryRowFaultDemo() {
// 查询单行数据
for i := 0; i < 100; i++ {
fmt.Printf("第%d次查询。\n", i)
// sql:select id,name,age from user where id=1;
sqlStr := "select id,name,age from user where id=1"
// 查询但是没有取结果 row会一直占用连接
row := DB.QueryRow(sqlStr)
fmt.Println(row)
continue
}
fmt.Println("查询结束!")
}
// 查询多条
func queryMultiDemo() {
var user User
sqlStr := "select id, name, age from user where id > ?"
rows, err := DB.Query(sqlStr, 0)
if err != nil {
fmt.Printf("query failed, err:%v\n", err)
return
}
defer func() {
rows.Close() // 会释放数据库连接
}()
// 循环读取数据
for rows.Next() {
err := rows.Scan(&user.id, &user.name, &user.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return
}
fmt.Printf("user:%#v\n", user)
}
}
// 插入数据示例
// sql:insert into user (name, age) values("赵凯", 18);
func insertDemo() {
sqlStr := "insert into user(name, age) values(?,?)"
name := "赵凯"
age := 18
// Exec:执行
ret, err := DB.Exec(sqlStr, name, age)
if err != nil {
fmt.Printf("insert failed, err:%v\n", err)
return
}
// 拿到刚插入的数据id值(不同的数据库有不同的实现)
theID, err := ret.LastInsertId()
if err != nil {
fmt.Printf("get lastinsertid failed, err:%v\n", err)
return
}
fmt.Println(theID)
}
// 更新数据
// 把官大妈那条数据的age字段 改成48
// sql:update user set age=? where id=?;
func updateDemo() {
sqlStr := "update user set age=? where id=?"
ret, err := DB.Exec(sqlStr, 48, 2)
if err != nil {
fmt.Printf("update failed, err:%v\n", err)
return
}
// 拿到受影响的行数
num, err := ret.RowsAffected()
if err != nil {
fmt.Printf("get affected row failed, err:%v\n", err)
return
}
fmt.Println("受影响行数:", num)
}
// 删除数据
// sql: delete from user where id=2;
func deleteDemo() {
sqlStr := "delete from user where id=?"
ret, err := DB.Exec(sqlStr, 2)
if err != nil {
fmt.Printf("deleter failed, err:%v\n", err)
return
}
num, err := ret.RowsAffected()
if err != nil {
fmt.Printf("get affected row failed, err:%v\n", err)
return
}
fmt.Println("受影响行数:", num)
}
func main() {
dsn := "root:root@tcp(127.0.0.1:3306)/sql_test"
err := initDB(dsn)
if err != nil {
fmt.Printf("init DB failed, err:%v\n", err)
return
}
// CRUD
// 查询单条
queryRowDemo()
//
// queryRowFaultDemo()
// 查询多条
// queryMultiDemo()
// 插入数据
// insertDemo()
// 更新数据
// updateDemo()
// 删除数据
// deleteDemo()
}
增删改查
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// MySQL预处理
// DB 数据库连接句柄,全局变量
var DB *sql.DB
// User 用户结构体
type User struct {
id int
name string
age string
}
func initDB(dsn string) (err error) {
DB, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
err = DB.Ping()
if err != nil {
return err
}
// 连接上数据库了
// 设置最大连接数
DB.SetMaxOpenConns(50)
// 设置最大的空闲连接数
// DB.SetMaxIdleConns(20)
return nil
}
func prepareInsertDemo() {
sqlStr := "insert into user (name,age) values(?,?)"
stmt, err := DB.Prepare(sqlStr) // 把要执行的命令发送给MySQL服务端做预处理
if err != nil {
fmt.Printf("prepare failed, err:%v\n", err)
return
}
defer stmt.Close()
// 执行重复的插入命令
for i := 0; i < 10; i++ {
name := fmt.Sprintf("stu%02d", i)
stmt.Exec(name, i)
}
}
// 预处理查询
func prepareQueryDemo() {
sqlStr := "select id,name,age from user where id=?"
stmt, err := DB.Prepare(sqlStr)
if err != nil {
fmt.Printf("prepare failed, err:%v\n", err)
return
}
defer stmt.Close()
for i := 0; i < 10; i++ {
rows, err := stmt.Query(i)
if err != nil {
fmt.Printf("query failed, err:%v\n", err)
continue
}
defer rows.Close()
var user User
for rows.Next() {
err := rows.Scan(&user.id, &user.name, &user.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return
}
fmt.Printf("user:%#v\n", user)
}
}
}
func main() {
dsn := "root:root@tcp(127.0.0.1:3306)/sql_test"
err := initDB(dsn)
if err != nil {
fmt.Printf("init DB failed, err:%v\n", err)
return
}
// 插入10条数据
// prepareInsertDemo()
// 查询10次
prepareQueryDemo()
}
预处理
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// MySQL预处理
// DB 数据库连接句柄,全局变量
var DB *sql.DB
// User 用户结构体
type User struct {
id int
name string
age string
}
func initDB(dsn string) (err error) {
DB, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
err = DB.Ping()
if err != nil {
return err
}
// 连接上数据库了
// 设置最大连接数
DB.SetMaxOpenConns(50)
// 设置最大的空闲连接数
// DB.SetMaxIdleConns(20)
return nil
}
// 事务
func transDemo() {
// 把id=1的用户年龄+2岁;把id=4的用户的年龄-2岁
tx, err := DB.Begin() // 开启事务
if err != nil {
fmt.Printf("begin trnas failed, err:%v\n", err)
if tx != nil {
tx.Rollback()
}
return
}
// 开始执行事务操作
sql1 := "update user set age=age+? where id=?"
_, err = tx.Exec(sql1, 2, 1) // 注意是tx.Exec()
if err != nil {
// 出错了,先回滚
tx.Rollback()
fmt.Printf("exec sql1 failed, err:%v\n", err)
return
}
sql2 := "update user set age=age-? where id=?"
_, err = tx.Exec(sql2, 2, 4)
if err != nil {
// 出错了,先回滚
tx.Rollback()
fmt.Printf("exec sql2 failed, err:%v\n", err)
return
}
// 如果走到这里说明上面两条sql都执行成功,可以提交事务了
err = tx.Commit()
if err != nil {
tx.Rollback()
fmt.Printf("commit failed, err:%v\n", err)
return
}
fmt.Println("两行记录更新成功!")
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/sql_test"
err := initDB(dsn)
if err != nil {
fmt.Printf("init DB failed, err:%v\n", err)
return
}
transDemo()
}
事务
Go操作MySQL之sqlx
第三方库sqlx
能够简化操作,提高开发效率
安装
go get github.com/jmoiron/sqlx
连接数据库
var db *sqlx.DB
func initDB() (err error) {
dsn := "user:password@tcp(127.0.0.1:3306)/dbname"
// 也可以使用MustConnect连接不成功就panic
db, err = sqlx.Connect("mysql", dsn)
if err != nil {
fmt.Printf("connect DB failed, err:%v\n", err)
return
}
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(10)
return
}
CRUD
package main
// sqlx 示例
import (
"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/go-sql-driver/mysql"
)
// DB 全局数据库连接对象(内置连接池的)
var DB *sqlx.DB
// User user表对应的结构体
type User struct {
ID int `db:"id"`
Name string `db:"name"`
Age int `db:"age"`
}
func initDB() (err error) {
dsn := "root:root@tcp(127.0.0.1:3306)/sql_test"
DB, err = sqlx.Connect("mysql", dsn)
if err != nil {
return
}
fmt.Println("连接数据库成功!")
return nil
}
// 查询单条
func queryRowDemo() {
sqlStr := "select id,name from user where id=?"
var user User
err := DB.Get(&user, sqlStr, 1)
if err != nil {
fmt.Printf("query failed, err:%v\n", err)
return
}
fmt.Printf("user:%#v\n", user)
}
// 查询多行
func queryMultiDemo() {
sqlStr := "select id,name,age from user where id >?"
var users []User
err := DB.Select(&users, sqlStr, 0)
if err != nil {
fmt.Printf("select failed, err:%v\n", err)
return
}
for _, user := range users {
fmt.Printf("user:%#v\n", user)
}
}
// 插入数据
func insertDemo() {
sqlStr := "insert into user(name, age) values (?,?)"
ret, err := DB.Exec(sqlStr, "每天起床美美哒", 19)
if err != nil {
fmt.Printf("insert failed, err:%v\n", err)
return
}
theID, err := ret.LastInsertId() // 新插入数据的id
if err != nil {
fmt.Printf("get lastinsert ID failed, err:%v\n", err)
return
}
fmt.Printf("insert success, the id is %d.\n", theID)
}
// 更新数据
func updateDemo() {
sqlStr := "update user set age=? where id=?"
ret, err := DB.Exec(sqlStr, 95, 3)
if err != nil {
fmt.Printf("update failed, err:%v\n", err)
return
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
fmt.Printf("get RowsAffected failed, err:%v\n", err)
return
}
fmt.Printf("update success, affected rows:%d\n", n)
}
// 删除数据
func deleteDemo() {
sqlStr := "delete from user where id = ?"
ret, err := DB.Exec(sqlStr, 1)
if err != nil {
fmt.Printf("delete failed, err:%v\n", err)
return
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
fmt.Printf("get RowsAffected failed, err:%v\n", err)
return
}
fmt.Printf("delete success, affected rows:%d\n", n)
}
// 事务操作
func transDemo() {
tx, err := DB.Beginx()
if err != nil {
if tx != nil {
tx.Rollback()
}
fmt.Printf("begin trnas failed, err:%v\n", err)
return
}
sql1 := "update user set age=age-? where id=?"
tx.MustExec(sql1, 2, 1) // 名字带Must的一般表示出错就panic:
sql2 := "update user set age=age+? where id=?"
tx.MustExec(sql2, 4, 2) // 名字带Must的一般表示出错就panic:
err = tx.Commit()
if err != nil {
tx.Rollback()
fmt.Printf("commit failed, err:%v\n", err)
}
fmt.Println("两条数据更新成功!")
}
func main() {
err := initDB()
if err != nil {
fmt.Printf("init DB failed, err:%v\n", err)
return
}
// 查询单条
// CRUD
// 查询单条
//queryRowDemo()
// 查询多条
//queryMultiDemo()
// 插入数据
//insertDemo()
// 更新数据
//updateDemo()
// 删除数据
deleteDemo()
//事务
//transDemo()
}
View Code
Go语言操作Redis
安装
Go语言中使用第三方库 https://github.com/go-redis/redis 连接Redis数据库并进行操作。使用以下命令下载并安装:
go get -u github.com/go-redis/redis
连接数据库
var redisdb *redis.Client
// 初始化连接
func initClient() (err error) {
redisdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
_, err = redisdb.Ping().Result()
if err != nil {
return err
}
fmt.Println("连接Redis数据库成功!")
return nil
}
CRUD
package main
// redis
import (
"fmt"
"github.com/go-redis/redis"
)
// 声明一个全局的redisdb变量
var redisdb *redis.Client
// 初始化连接
func initClient() (err error) {
redisdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
_, err = redisdb.Ping().Result()
if err != nil {
return err
}
fmt.Println("连接Redis数据库成功!")
return nil
}
func main() {
err := initClient()
if err != nil {
fmt.Printf("connect redis failed, err:%v\n", err)
}
ret := redisdb.Get("age").Val()
fmt.Println(ret)
}
demo
func redisExample() {
err := redisdb.Set("score", 100, 0).Err()
if err != nil {
fmt.Printf("set score failed, err:%v\n", err)
return
}
val, err := redisdb.Get("score").Result()
if err != nil {
fmt.Printf("get score failed, err:%v\n", err)
return
}
fmt.Println("score", val)
val2, err := redisdb.Get("name").Result()
if err == redis.Nil {
fmt.Println("name does not exist")
} else if err != nil {
fmt.Printf("get name failed, err:%v\n", err)
return
} else {
fmt.Println("name", val2)
}
}
set/get示例
func redisExample2() {
zsetKey := "language_rank"
languages := []*redis.Z{
&redis.Z{Score: 90.0, Member: "Golang"},
&redis.Z{Score: 98.0, Member: "Java"},
&redis.Z{Score: 95.0, Member: "Python"},
&redis.Z{Score: 97.0, Member: "JavaScript"},
&redis.Z{Score: 99.0, Member: "C/C++"},
}
// ZADD
num, err := redisdb.ZAdd(zsetKey, languages...).Result()
if err != nil {
fmt.Printf("zadd failed, err:%v\n", err)
return
}
fmt.Printf("zadd %d succ.\n", num)
// 把Golang的分数加10
newScore, err := redisdb.ZIncrBy(zsetKey, 10.0, "Golang").Result()
if err != nil {
fmt.Printf("zincrby failed, err:%v\n", err)
return
}
fmt.Printf("Golang's score is %f now.\n", newScore)
// 取分数最高的3个
ret, err := redisdb.ZRevRangeWithScores(zsetKey, 0, 2).Result()
if err != nil {
fmt.Printf("zrevrange failed, err:%v\n", err)
return
}
for _, z := range ret {
fmt.Println(z.Member, z.Score)
}
// 取95~100分的
op := &redis.ZRangeBy{
Min: "95",
Max: "100",
}
ret, err = redisdb.ZRangeByScoreWithScores(zsetKey, op).Result()
if err != nil {
fmt.Printf("zrangebyscore failed, err:%v\n", err)
return
}
for _, z := range ret {
fmt.Println(z.Member, z.Score)
}
}
zset示例