go【第十一篇】操作数据库

Go操作MySQL之database/sql

这种方式是Go内置的操作MySQL的方式,有点复杂,平时很少使用这里只做大概介绍,了解下即可。

连接

Go语言中的database/sql包提供了保证SQL或类SQL数据库的泛用接口,并不提供具体的数据库驱动。使用database/sql包时必须注入(至少)一个数据库驱动。

我们常用的数据库基本上都有完整的第三方实现。例如:MySQL驱动 

下载依赖

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

官方文档

https://godoc.org/github.com/jmoiron/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

官方文档

https://godoc.org/github.com/go-redis/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示例

 

posted @ 2019-07-13 09:34  沐风先生  阅读(375)  评论(0编辑  收藏  举报