Go 数据库操作

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

git源码地址

https://gitee.com/rush_peng/gin_-reddit_demo.git

Go 数据库操作

上一篇:解决docker安装mysql8.0无法远程连接问题


下一篇:mysql复杂查询