MySQL数据库(使用原始包)
尝试连接
package main
import (
"database/sql" // sql接口
"fmt"
_ "github.com/go-sql-driver/mysql" //使用了 init() 方法
"time"
)
var db *sql.DB
func initMySQL() (err error) {
// data source Name
dsn := "root:123456@tcp(192.168.107.131:3306)/gindemo"
//db, err := sql.Open("mysql", dsn) //这个是声明一个新的变量,而不是初始化全局变量
db, err = sql.Open("mysql", dsn) // 这个地方要去掉冒号
if err != nil {
panic(err)
}
// 要写在err判断的下面,确保 db 不为 nil,这句话也可以放到最下面
//defer db.Close()
fmt.Println("能够连上数据库")
// 真正的与数据库连接
err = db.Ping()
if err != nil {
fmt.Println("连接识别,错误信息为:", err)
return err
}
fmt.Println("连接数据成功")
db.SetConnMaxLifetime(time.Second * 10)
db.SetMaxOpenConns(200) //最大连接数
db.SetMaxIdleConns(10) // 最大空闲连接数
return nil // 没有问题就返回nil
}
func main() {
if err := initMySQL(); err != nil {
fmt.Println("数据库连接成功")
}
defer db.Close() // 关闭db链接
}
增删改查的操作
单行查询
func queryRowDemo() {
sqlstr := "select id,name,age from user where id=?"
var u user
err := db.QueryRow(sqlstr, 1).Scan(&u.id, &u.name, &u.age) //如果没有scan操作,那么就不会释放某连接
if err != nil {
fmt.Println("查询uer有误", err)
return
}
fmt.Println("查询到的user:", u)
}
多行查询
func queryMultiRowDemo() {
sqlstr := "select id,name,age from user where id>?"
rows, err := db.Query(sqlstr, 0)
if err != nil {
fmt.Println("多行查询失败", err)
}
defer rows.Close() // 一定要释放rows的连接池
// 多行查询
for rows.Next() { //只要有,就会一直循环下去
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Println("scan failed,err", err)
}
fmt.Println(u)
}
}
插入
func insertRowDemo() {
sqlstr := "insert into user (name,age) values(?,?)"
ret, err := db.Exec(sqlstr, "王五", 5)
if err != nil {
fmt.Println("插入失败", err)
return
}
theid, err := ret.LastInsertId() // 新插入数据的id
if err != nil {
fmt.Println("获得最后插入用户失败", err)
return
}
fmt.Println("获得最后插入用成功", theid)
}
更新
func updateRowDemo() {
sqlstr := "update user set age=? where id=?"
ret, err := db.Exec(sqlstr, 28, 1)
if err != nil {
fmt.Println("更新用户失败", err)
return
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
fmt.Println("获得最后更新用户失败", err)
return
}
fmt.Println("获得最后更新用户成功,并且受影响的行数为", n)
}
删除
func deletRowDemo() {
sqlstr := "delete from user where id = ?"
ret, err := db.Exec(sqlstr, 10)
if err != nil {
fmt.Println("删除有错误", err)
return
}
n, err := ret.RowsAffected() // 返回受影响的行数
if err != nil {
fmt.Println("返回受影响函数有错", err)
}
fmt.Println("删除时候,收影响的行数为", n)
}
SQL预处理
sql的预处理,就是把数据和命令分开,先把命令部分发送给mysql
数据库,然后再把传数据过去,这样可以实现一次编译,多次执行,节约后续的编译成本。这种方式适合于一种sql语句,多次查询的环境
// 预处理
func prepareWuerDemo() {
sqlstr := "select id,name,age from user where id>?"
stmt, err := db.Prepare(sqlstr) // 预处理指令
if err != nil {
fmt.Println("预处理命令发送错误")
return
}
defer stmt.Close()
rows, err := stmt.Query(0)
if err != nil {
fmt.Println("错误")
}
defer rows.Close()
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Println("哈哈")
}
fmt.Println(u)
}
}
sql注入语句
// sql注入
func SqlInjectDemo(name string) {
sqlstr := fmt.Sprintf("select id,name,age from user where name=‘%s‘", name)
var u user
fmt.Println(sqlstr)
err := db.QueryRow(sqlstr).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Println("sql注入连接失败")
}
fmt.Println(u)
}
func main() {
if err := initMySQL(); err != nil {
fmt.Println("数据库连接成功")
}
defer db.Close() // 关闭db链接
SqlInjectDemo("xxx‘ or 1=1#")
}
MySQL事务
// 事务
func transactionDemo() {
tx, err := db.Begin() // 开启事务
if err != nil {
if tx != nil {
tx.Rollback() // 回滚
}
fmt.Printf("begin transaction failed err:%v\n", err)
return
}
sqlStr1 := "update user set age=18 where id=?"
ret1, err := tx.Exec(sqlStr1, 2)
if err != nil {
tx.Rollback() // 回滚
return
}
affrow1, _ := ret1.RowsAffected() // 第一条语句受影响的行数
sqlStr2 := "update user set age=50 where id=?"
ret2, err := tx.Exec(sqlStr2, 1)
if err != nil {
tx.Rollback() // 回滚
return
}
affrow2, err := ret2.RowsAffected() //第二行的影响的行数
if affrow1 == 1 && affrow2 == 1 { //当受影响的结果都为1的时候,就返回事务
err := tx.Commit() // 提交事务
if err != nil {
fmt.Println("事务提交成功")
} else {
fmt.Println("提交事务失败")
}
} else {
fmt.Println("提交事务失败", err)
tx.Rollback()
return
}
}
sqlx库操作 MySQL 数据库
连接数据库,和原生的sql相比,直接一步操作了
import (
"errors"
"fmt"
"github.com/jmoiron/sqlx"
)
var dbx *sqlx.DB
//初始化连接
func initDBX() (err error) {
dsn := "root:123456@tcp(192.168.107.131:3306)/gindemo?charset=utf8mb4&parseTime=True"
dbx, err = sqlx.Connect("mysql", dsn) // 这个直接连接上
if err != nil {
fmt.Println("测试连接识别", err)
return err
}
dbx.SetMaxOpenConns(20) // 最大的
dbx.SetMaxIdleConns(10) // 闲置的
fmt.Println("****dbx方式连接数据库成功*****")
return
}
单行查询
和原生 sql 相比,可以直接将查询的结果赋值给结构体。但是注意,这种写法的时候,结构体里的成员名要大些,因为他要传到sqlx的函数体内进行赋值
func XqueryRowDemo() {
sqlstr1 := "select id,name,age from user where id=?"
var u User
if err := dbx.Get(&u, sqlstr1, 1); err != nil {
fmt.Println("赋值失败")
}
fmt.Println(u)
}
结构体的成员声明要用大写
type User struct {
Id int `db:"id"`
Name string `db:"name"`
Age int `db:"age"`
}
多行查询
可以直接把结果赋值给一个数组
func XquerMultiRowDemo() {
sqltr := "select id,name,age from user where id>?"
var users []User
if err := dbx.Select(&users, sqltr, 0); err != nil {
fmt.Println("多条查询有误", err)
return
}
fmt.Println(users)
}
插入,修改,删除等和原始的基本一样
下面以插入为例
func XInsertRowDemo() {
sqltr := "insert into user (name,age) values(?,?)"
ret, err := dbx.Exec(sqltr, "小刘", 14)
if err != nil {
fmt.Println("插入数据有误", err)
}
if theId, err := ret.LastInsertId(); err != nil { //最后收影响的行数
fmt.Println("插入数据有误", err)
} else {
fmt.Println("插入成功,受影响的Id", theId)
}
}
sqlX 的事务
sqlx 的事务,主要是在 defer
函数里使用了回滚和条件判断
// 事务
func transactionDemo2() (err error) {
tx, err := dbx.Beginx() // 开启事务
if err != nil {
fmt.Println("事务开启失败", err)
return err
}
defer func() {
if p := recover(); p != nil {
tx.Rollback() //先修复一下,修复不了再回滚
panic(p)
} else if err != nil {
fmt.Println("roolback")
tx.Rollback() // 回滚
} else {
err = tx.Commit()
fmt.Println("commit")
}
}()
sqlstr := "update user set age=20 where id=?"
rs, err := tx.Exec(sqlstr, 1)
if err != nil {
return err
}
affectedRows, err := rs.RowsAffected() //受影响的行数
if affectedRows != 1 {
return errors.New("结果不同步")
}
return
}
go-redis库
连接redis
下面只是一些简单的操作,除之外还有事务
watch
pipleline
package main
import (
"fmt"
"github.com/go-redis/redis"
)
var rdb *redis.Client
//初始化连接
func initClient() (err error) {
rdb = redis.NewClient(&redis.Options{
Addr: "192.168.107.131:6379",
Password: "123456",
DB: 0, //使用的
PoolSize: 100, //连接池大小
})
if _, err := rdb.Ping().Result(); err != nil {
fmt.Println("redis连接失败")
return err
}
fmt.Println("连接成功")
return
}
// 连接哨兵
// 连接集群
// set和get的操作
func redisSetGet() {
if err := rdb.Set("score", 100, 0).Err(); err != nil {
fmt.Println("set score failed ,err:", err)
return
}
//获取一个存在的值
val, err := rdb.Get("score").Result()
if err != nil {
fmt.Println("读取score错误", err)
} else {
fmt.Println("成功读取了score的值", val)
}
//获取一个并不存在的值
val2, err := rdb.Get("name").Result()
if err == redis.Nil {
fmt.Println("你取得为空值")
} else if err != nil {
fmt.Println("取出参数错误")
} else {
fmt.Println("读取成功", val2)
}
}
//使用pipleline,即将客户端缓冲的一堆命令,一次性的发送给服务器,
//这些命令不能保证在事务中执行,但是这样做的好处就是节省了每个命令的网络往返时间
func main() {
initClient()
redisSetGet()
defer func() { // 程序退出的时候,
rdb.Close()
}()
}