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
}
}
其中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() }
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) }
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) } }
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) } }