go这些基础的东西,看起来很舒服,最起码对于写习惯java的人来讲,go真的很舒服,所以近一段时间可能一直连更,gin并没有直接封装操作MySQL的工具类,所以还是使用开源的工具

github.com/garyburd/redigo v1.6.2
github.com/go-sql-driver/mysql v1.5.0
github.com/jmoiron/sqlx v1.2.0

go操作mysql数据库

当前代码文件结构

|- mian.go
|- app 
|	|- api
|		|- student_api.go
|	|- model
|		|- student
|			|- student.go
|- routers
|	|- routers.go
|- util
|	|- util.go
新建 myschool数据库,以及student
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `id` varchar(225) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `name` varchar(225) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `age` int(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
获取数据库连接

对比goframe框架的话,gin看起来不是很整洁,因为没有封装操作数据库的工具,所以我们只能用原生的代码来操作,需要写死配置,对比JavaSpringBoot确实差一点,但是对比JDBC的话,go可就好太多了

这里的话,新建util.go用于获取数据库连接,将路由、功能、工具区分开

var Db *sqlx.DB

func Init() {
	// 获取 MySQL 链接需要自己导入 _ "github.com/go-sql-driver/mysql" 
	database, err := sqlx.Open("mysql", "root:Root5683@@tcp(127.0.0.1:3306)/myschool")
	//database, err := sqlx.Open("数据库类型", "用户名:密码@tcp(地址:端口)/数据库名")
	if err != nil {
		fmt.Println("open mysql failed,", err)
		return
	}

	Db = database
	// 关闭数据库连接
	//defer database.Close() // 注意这行代码要写在上面err判断的下面
}

连接创建完成下一步,我们需要创建路由,这里选择分组路由,方便后面继续做拓展

创建路由

创建routers.go

func Router() *gin.Engine {
	r := gin.Default()
	// 初始化数据连接
	util.Init()
	s := r.Group("/student")
	{
		s.PUT("/save", api.Save)
		s.GET("/select", api.SelectById)
		s.POST("/update", api.Update)
		s.DELETE("/delete", api.Delete)
	}
	return r
}

启动类肯定是必不可少的,接下来看下一下main.go

func main() {
	// 加载路由
	r := routers.Router()
    // 默认端口:8080
	r.Run()
}

结构实体肯定是必不可少的,student.go,这里就要夸一夸goframe框架了,可以自动生成,但是没有的话,自己麻烦一点,也就麻烦那一下,也不是说很多个字段那么夸张

type Student struct {
	Id   string `orm:"id,primary" json:"id"`
	Name string `orm:"name"       json:"name"`
	Age  int    `orm:"age"        json:"age"`
}
crud

准备工作做好,下面就是重点了,对student表进行操作crud,接口测试使用Postman

// 新增学生
/*
http://127.0.0.1:8080/student/save
{
   "id":"1",
   "name":"chenghao_",
   "age":30
}
*/
func Save(c *gin.Context) {
	// 获取传递的参数 转换成 struct
	var stu student.Student
	if err := c.ShouldBindJSON(&stu); err != nil {
		// 返回错误信息
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	result, err := util.Db.Exec("insert into student(id,name,age) values(?,?,?)", stu.Id, stu.Name, stu.Age)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	log.Println(result)
	c.JSON(http.StatusOK, gin.H{"success": "添加成功"})
}

// 根据 id 查询
/*
http://127.0.0.1:8080/student/select?id=1
*/
func SelectById(c *gin.Context) {
	var stu []student.Student
	// 获取查询参数
	id := c.Query("id")
	err := util.Db.Select(&stu, "select id,name,age from student where id =?", id)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": stu})
}

// 修改
/*
http://127.0.0.1:8080/student/update
{
   "id":"1",
   "name":"chenghao_",
   "age":19
}
*/
func Update(c *gin.Context) {
	// 获取传递的参数 转换成你 struct
	var stu student.Student
	if err := c.ShouldBindJSON(&stu); err != nil {
		// 返回错误信息
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	result, err := util.Db.Exec("update student set name=?,age=? where id=?", stu.Name, stu.Age, stu.Id)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	log.Println(result)
	c.JSON(http.StatusOK, gin.H{"success": "修改成功"})
}

// 删除
/*
http://127.0.0.1:8080/student/select?id=1
*/
func Delete(c *gin.Context) {
	// 获取查询参数
	id := c.Query("id")
	result, err := util.Db.Exec("delete from student where id =?", id)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": result})
}

go也相应的支持MySQL事务,看业务逻辑自行调用

1. Db.Begin()        开始事务
2. Db.Commit()        提交事务
3. Db.Rollback()     回滚事务

go操作Redis

Redis 高性能的key-value数据库,需要了解的自行学习,这里只做简单的使用

获取数据链接

func main() {
    c, err := redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		log.Println("conn redis failed,", err)
		return
	}
}
String类型

使用c.Do(commandName string, args ...interface{})向服务器发送命令并返回收到的答复,低一个是操作名称,这个和redis原生命令有点像,只是命令首字母是大写的,javajedis有点类似,只是jedis是直接点set()方法,且首字母小写

redis操作String类型,

func main() {
    // 获取操作redis的链接
    c, err := redis.Dial("tcp", "localhost:6379")
	if err != nil {
		log.Println("conn redis failed,", err)
		return
	}

    // 设置一个key为name 值为Shao Jie
	_, err = c.Do("Set", "name", "Shao Jie")
	if err != nil {
		log.Println(err.Error())
		return
	}
	// 取值 取一个key为name的值
	name, err := redis.String(c.Do("Get", "name"))
	if err != nil {
		log.Println(err.Error())
		return
	}
	log.Println(name)
}

需要注意一个问题,c.Do方法在操作Get的时候,会出现乱码,这里用redis.String转了一下类型,正常输出,如果是一个数字类型可以调用redis.Int

批量操作String类型
// 设置一个key是name一个为age 值为Shao Jie、21
_, err = c.Do("MSet", "name", "shaojie_", "age", 21)
if err != nil {
    log.Println(err.Error())
    return
}

// 取值 取一个key为name一个为age的值
result, err := redis.Strings(c.Do("MGet", "name", "age"))
if err != nil {
    log.Println("get name failed,", err)
    return
}

for _, v := range result {
    log.Println(v)
}

需要注意,这里我们取值的时候取的是批量的,所以,这里要注意转型redis.Strings,数字类型调用redis.Ints

设置过期时间
// 设置一个key为name 值为shaojie_
_, err = c.Do("Set", "name", "shaojie_")
if err != nil {
    log.Println(err.Error())
    return
}

// 设置过期时间 10 为秒
_, err = c.Do("expire", "name", 10)
if err != nil {
    fmt.Println(err)
    return
}

// 取值 取一个key为name的值
name, err := redis.String(c.Do("Get", "name"))
if err != nil {
    log.Println(err.Error())
    return
}
log.Println(name)

// 休眠十秒
time.Sleep(10 * time.Second)

// 取值  十秒取一个key为name的值
name1, err := redis.String(c.Do("Get", "name"))
if err != nil {
    log.Println(err.Error())
    return
}
log.Println(name1)

这里显得比java要麻烦一些,需要单独使用expire为特定的key设置过期时间,单位是秒

list类型
// 设置一个key为namelist 值为shaojie_ 、chenghao_
_, err = c.Do("lpush", "namelist", "shaojie_","chenghao_")
if err != nil {
    log.Println(err.Error())
    return
}

// 取值 获取一个值为 namelist的值 获取这个值之后会删除这个值
namelist, err := redis.String(c.Do("lpop", "namelist"))
if err != nil {
    log.Println("get namelist failed,", err)
    return
}

log.Println(namelist)
hash类型
// 设置 shaojie_ 的年龄age为21
_, err = c.Do("HSet", "shaojie_", "age", 21)
if err != nil {
    log.Println(err)
    return
}

// 获取 shaojie_ 的年龄age
age, err := redis.Int(c.Do("HGet", "shaojie_", "age"))
if err != nil {
    fmt.Println("get shaojie_'s age failed,", err)
    return
}

log.Println(age)

以上三种类型是我们常会用到的,后面都需要获取redis的连接显得有些麻烦,不可能每次使用都去连接一次,如果想像操作mysql一样只获取一次,然后直接可以在别得地方调用,那就很舒服,所以,可以借用redis连接池

redis连接池

获取redis连接池,这个我们同样在util.go中得Init方法中调用

var Pool *redis.Pool //创建redis连接池

func Init() {
	Pool = &redis.Pool{ //实例化一个连接池
		MaxIdle: 16, //最初的连接数量
		// MaxActive:1000000,    //最大连接数量
		MaxActive:   0,   //连接池最大连接数量,不确定可以用0(0表示自动定义),按   需分配
		IdleTimeout: 300, //连接关闭时间 300秒 (300秒不使用自动关闭)
		Dial: func() (redis.Conn, error) { //要连接的redis数据库
			return redis.Dial("tcp", "127.0.0.1:6379")
		},
	}
}

创建好工具后,需要在系统启动时调用,去创建连接池,然后再以hash类型举例,获取在上面设置好的一个key的值

func main() {
    // 初始化工具的连接
	util.Init()
	c := util.Pool.Get() //从连接池,取一个链接
	defer c.Close() //函数运行结束 ,把连接放回连接池

    // 拿key为shaojie_的age的具体值
	r, err := redis.Int(c.Do("HGet", "shaojie_","age"))
	if err != nil {
		fmt.Println("get abc faild :", err)
		return
	}
	log.Println(r)
	util.Pool.Close() //关闭连接池
}

这一整个的流程文档讲的还算比较清楚,对比java学习事半功倍,最近学习起来感觉极其上瘾,但是平常公司有点小忙,也算是忙里偷闲去学习,多学习才会有更多的选择,学无止境,苦海无涯

借鉴一位学长的话:

每天要学两个小时。 --小石头

这句话其实给我说了很久,我很少有机会去做,但是却没有忘记,没有理由没有借口,如果习惯去找借口,那学习这件事情不如就此放弃,共勉!